Dieses Codelab ist Teil des Kotlin-Bootcamps für Programmierer. Sie können diesen Kurs am besten nutzen, wenn Sie die Codelabs der Reihe nach durcharbeiten. Je nach Ihrem Wissen können Sie einige Abschnitte möglicherweise überfliegen. Dieser Kurs richtet sich an Programmierer, die eine objektorientierte Sprache kennen und Kotlin lernen möchten.
Einführung
In diesem Codelab erstellen Sie ein Kotlin-Programm und lernen Klassen und Objekte in Kotlin kennen. Vieles davon ist Ihnen vielleicht schon aus anderen objektorientierten Sprachen bekannt. Kotlin weist jedoch einige wichtige Unterschiede auf, die die Menge an Code, die Sie schreiben müssen, reduzieren. Außerdem erfahren Sie mehr über abstrakte Klassen und die Delegierung von Schnittstellen.
In diesem Kurs wird keine einzelne Beispiel-App entwickelt. Stattdessen sollen die Lektionen Ihr Wissen erweitern, sind aber weitgehend unabhängig voneinander, sodass Sie Abschnitte, mit denen Sie vertraut sind, überspringen können. Um die Beispiele zu veranschaulichen, wird in vielen ein Aquarium verwendet. Wenn Sie die ganze Geschichte des Aquariums sehen möchten, können Sie sich den Udacity-Kurs Kotlin Bootcamp for Programmers ansehen.
Was Sie bereits wissen sollten
- Die Grundlagen von Kotlin, einschließlich Typen, Operatoren und Schleifen
- Kotlin-Funktionssyntax
- Grundlagen der objektorientierten Programmierung
- Grundlagen einer IDE wie IntelliJ IDEA oder Android Studio
Lerninhalte
- Klassen erstellen und auf Attribute in Kotlin zugreifen
- Klassenkonstruktoren in Kotlin erstellen und verwenden
- Unterklasse erstellen und Funktionsweise der Übernahme
- Abstrakte Klassen, Schnittstellen und Schnittstellendelegierung
- Datenklassen erstellen und verwenden
- Singletons, Enums und versiegelte Klassen verwenden
Aufgaben
- Klasse mit Attributen erstellen
- Konstruktor für eine Klasse erstellen
- Unterklasse erstellen
- Beispiele für abstrakte Klassen und Schnittstellen ansehen
- Einfache Datenklasse erstellen
- Informationen zu Singletons, Enums und versiegelten Klassen
Die folgenden Programmierbegriffe sollten Ihnen bereits bekannt sein:
- Klassen sind Baupläne für Objekte. Eine
Aquarium
-Klasse ist beispielsweise der Bauplan für die Erstellung eines Aquariums. - Objekte sind Instanzen von Klassen. Ein Aquarium-Objekt ist ein tatsächliches
Aquarium
. - Eigenschaften sind Merkmale von Klassen, z. B. Länge, Breite und Höhe eines
Aquarium
. - Methoden, auch Memberfunktionen genannt, sind die Funktionalität der Klasse. Methoden sind die Aktionen, die Sie mit dem Objekt ausführen können. Sie können beispielsweise ein
fillWithWater()
-ObjektAquarium
. - Eine Schnittstelle ist eine Spezifikation, die eine Klasse implementieren kann. Die Reinigung ist beispielsweise für andere Objekte als Aquarien üblich und erfolgt im Allgemeinen auf ähnliche Weise für verschiedene Objekte. Sie könnten also eine Schnittstelle mit dem Namen
Clean
haben, die eineclean()
-Methode definiert. Die KlasseAquarium
könnte die SchnittstelleClean
implementieren, um das Aquarium mit einem weichen Schwamm zu reinigen. - Pakete sind eine Möglichkeit, zusammengehörigen Code zu gruppieren, um ihn zu organisieren oder eine Codebibliothek zu erstellen. Nachdem ein Paket erstellt wurde, können Sie seinen Inhalt in eine andere Datei importieren und den Code und die Klassen darin wiederverwenden.
In dieser Aufgabe erstellen Sie ein neues Paket und eine Klasse mit einigen Attributen und einer Methode.
Schritt 1: Paket erstellen
Mit Paketen können Sie Ihren Code organisieren.
- Klicken Sie im Bereich Project (Projekt) unter dem Projekt Hello Kotlin (Hallo Kotlin) mit der rechten Maustaste auf den Ordner src.
- Wählen Sie Neu > Paket aus und nennen Sie es
example.myapp
.
Schritt 2: Klasse mit Attributen erstellen
Klassen werden mit dem Keyword class
definiert und Klassennamen beginnen üblicherweise mit einem Großbuchstaben.
- Klicken Sie mit der rechten Maustaste auf das Paket example.myapp.
- Wählen Sie New > Kotlin File / Class (Neu > Kotlin-Datei / -Klasse) aus.
- Wählen Sie unter Art die Option Kurs aus und geben Sie einen Namen für den Kurs ein
Aquarium
. IntelliJ IDEA fügt den Paketnamen in die Datei ein und erstellt eine leereAquarium
-Klasse für Sie. - Definieren und initialisieren Sie in der Klasse
Aquarium
dievar
-Attribute für Breite, Höhe und Länge (in Zentimetern). Initialisieren Sie die Attribute mit Standardwerten.
package example.myapp
class Aquarium {
var width: Int = 20
var height: Int = 40
var length: Int = 100
}
Im Hintergrund erstellt Kotlin automatisch Getter- und Setter-Methoden für die Attribute, die Sie in der Klasse Aquarium
definiert haben. Sie können also direkt auf die Attribute zugreifen, z. B. myAquarium.length
.
Schritt 3: main()-Funktion erstellen
Erstellen Sie eine neue Datei mit dem Namen main.kt
, in der die Funktion main()
enthalten ist.
- Klicken Sie im Bereich Project (Projekt) auf der linken Seite mit der rechten Maustaste auf das Paket example.myapp.
- Wählen Sie New > Kotlin File / Class (Neu > Kotlin-Datei / -Klasse) aus.
- Lassen Sie im Drop-down-Menü Art die Auswahl Datei bei und geben Sie der Datei den Namen
main.kt
. IntelliJ IDEA enthält den Paketnamen, aber keine Klassendefinition für eine Datei. - Definieren Sie eine
buildAquarium()
-Funktion und erstellen Sie darin eine Instanz vonAquarium
. Um eine Instanz zu erstellen, verweisen Sie auf die Klasse, als wäre sie eine Funktion:Aquarium()
. Dadurch wird der Konstruktor der Klasse aufgerufen und eine Instanz derAquarium
-Klasse erstellt, ähnlich wie bei der Verwendung vonnew
in anderen Sprachen. - Definieren Sie eine
main()
-Funktion und rufen SiebuildAquarium()
auf.
package example.myapp
fun buildAquarium() {
val myAquarium = Aquarium()
}
fun main() {
buildAquarium()
}
Schritt 4: Methode hinzufügen
- Fügen Sie in der Klasse
Aquarium
eine Methode zum Ausgeben der Dimensionsattribute des Aquariums hinzu.
fun printSize() {
println("Width: $width cm " +
"Length: $length cm " +
"Height: $height cm ")
}
- Rufen Sie in
main.kt
inbuildAquarium()
die MethodeprintSize()
fürmyAquarium
auf.
fun buildAquarium() {
val myAquarium = Aquarium()
myAquarium.printSize()
}
- Führen Sie Ihr Programm aus, indem Sie auf das grüne Dreieck neben der Funktion
main()
klicken. Sehen Sie sich das Ergebnis an.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm
- Fügen Sie in
buildAquarium()
Code hinzu, um die Höhe auf 60 festzulegen und die geänderten Dimensionseigenschaften auszugeben.
fun buildAquarium() {
val myAquarium = Aquarium()
myAquarium.printSize()
myAquarium.height = 60
myAquarium.printSize()
}
- Führen Sie das Programm aus und beobachten Sie die Ausgabe.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm Width: 20 cm Length: 100 cm Height: 60 cm
In dieser Aufgabe erstellen Sie einen Konstruktor für die Klasse und arbeiten weiter mit Properties.
Schritt 1: Konstruktor erstellen
In diesem Schritt fügen Sie der Klasse Aquarium
, die Sie in der ersten Aufgabe erstellt haben, einen Konstruktor hinzu. Im vorherigen Beispiel wird jede Instanz von Aquarium
mit denselben Dimensionen erstellt. Sie können die Abmessungen nach dem Erstellen durch Festlegen der Eigenschaften ändern. Es ist jedoch einfacher, die richtige Größe von Anfang an festzulegen.
In einigen Programmiersprachen wird der Konstruktor definiert, indem eine Methode in der Klasse erstellt wird, die denselben Namen wie die Klasse hat. In Kotlin definieren Sie den Konstruktor direkt in der Klassendeklaration selbst und geben die Parameter in Klammern an, als wäre die Klasse eine Methode. Wie bei Funktionen in Kotlin können diese Parameter Standardwerte enthalten.
- Ändern Sie in der zuvor erstellten Klasse
Aquarium
die Klassendefinition so, dass sie drei Konstruktorparameter mit Standardwerten fürlength
,width
undheight
enthält, und weisen Sie sie den entsprechenden Eigenschaften zu.
class Aquarium(length: Int = 100, width: Int = 20, height: Int = 40) {
// Dimensions in cm
var length: Int = length
var width: Int = width
var height: Int = height
...
}
- In Kotlin können Sie die Eigenschaften kompakter direkt mit dem Konstruktor definieren, indem Sie
var
oderval
verwenden. Außerdem werden die Getter und Setter automatisch erstellt. Anschließend können Sie die Attributdefinitionen im Text der Klasse entfernen.
class Aquarium(var length: Int = 100, var width: Int = 20, var height: Int = 40) {
...
}
- Wenn Sie ein
Aquarium
-Objekt mit diesem Konstruktor erstellen, können Sie entweder keine Argumente angeben und die Standardwerte verwenden, nur einige Argumente angeben oder alle Argumente angeben und einAquarium
-Objekt mit einer vollständig benutzerdefinierten Größe erstellen. Probieren Sie in der FunktionbuildAquarium()
verschiedene Möglichkeiten aus, einAquarium
-Objekt mit benannten Parametern zu erstellen.
fun buildAquarium() {
val aquarium1 = Aquarium()
aquarium1.printSize()
// default height and length
val aquarium2 = Aquarium(width = 25)
aquarium2.printSize()
// default width
val aquarium3 = Aquarium(height = 35, length = 110)
aquarium3.printSize()
// everything custom
val aquarium4 = Aquarium(width = 25, height = 35, length = 110)
aquarium4.printSize()
}
- Führen Sie das Programm aus und sehen Sie sich die Ausgabe an.
⇒ Width: 20 cm Length: 100 cm Height: 40 cm Width: 25 cm Length: 100 cm Height: 40 cm Width: 20 cm Length: 110 cm Height: 35 cm Width: 25 cm Length: 110 cm Height: 35 cm
Sie mussten den Konstruktor nicht überladen und für jeden dieser Fälle (und einige weitere für die anderen Kombinationen) eine andere Version schreiben. Kotlin erstellt das, was benötigt wird, aus den Standardwerten und benannten Parametern.
Schritt 2: Initialisierungsblöcke hinzufügen
In den Beispielkonstruktoren oben werden nur Eigenschaften deklariert und ihnen wird der Wert eines Ausdrucks zugewiesen. Wenn Ihr Konstruktor mehr Initialisierungscode benötigt, kann er in einem oder mehreren init
-Blöcken platziert werden. In diesem Schritt fügen Sie der Klasse Aquarium
einige init
-Blöcke hinzu.
- Fügen Sie in der Klasse
Aquarium
eineninit
-Block hinzu, um auszugeben, dass das Objekt initialisiert wird, und einen zweiten Block, um das Volumen in Litern auszugeben.
class Aquarium (var length: Int = 100, var width: Int = 20, var height: Int = 40) {
init {
println("aquarium initializing")
}
init {
// 1 liter = 1000 cm^3
println("Volume: ${width * length * height / 1000} l")
}
}
- Führen Sie das Programm aus und sehen Sie sich die Ausgabe an.
aquarium initializing
Volume: 80 l
Width: 20 cm Length: 100 cm Height: 40 cm
aquarium initializing
Volume: 100 l
Width: 25 cm Length: 100 cm Height: 40 cm
aquarium initializing
Volume: 77 l
Width: 20 cm Length: 110 cm Height: 35 cm
aquarium initializing
Volume: 96 l
Width: 25 cm Length: 110 cm Height: 35 cm
Die init
-Blöcke werden in der Reihenfolge ausgeführt, in der sie in der Klassendefinition aufgeführt sind. Alle werden ausgeführt, wenn der Konstruktor aufgerufen wird.
Schritt 3: Sekundäre Konstruktoren
In diesem Schritt erfahren Sie mehr über sekundäre Konstruktoren und fügen Ihrer Klasse einen hinzu. Zusätzlich zu einem primären Konstruktor, der einen oder mehrere init
-Blöcke haben kann, kann eine Kotlin-Klasse auch einen oder mehrere sekundäre Konstruktoren haben, um die Überladung von Konstruktoren zu ermöglichen, d. h. Konstruktoren mit unterschiedlichen Argumenten.
- Fügen Sie der Klasse
Aquarium
einen sekundären Konstruktor hinzu, der mit dem Schlüsselwortconstructor
eine Anzahl von Fischen als Argument akzeptiert. Erstelle eineval
-Tank-Property für das berechnete Volumen des Aquariums in Litern basierend auf der Anzahl der Fische. Rechnen Sie mit 2 Litern (2.000 cm³) Wasser pro Fisch und etwas zusätzlichem Platz, damit das Wasser nicht verschüttet wird.
constructor(numberOfFish: Int) : this() {
// 2,000 cm^3 per fish + extra room so water doesn't spill
val tank = numberOfFish * 2000 * 1.1
}
- Behalten Sie im sekundären Konstruktor die Länge und Breite bei, die im primären Konstruktor festgelegt wurden, und berechnen Sie die Höhe, die erforderlich ist, um das angegebene Volumen zu erreichen.
// calculate the height needed
height = (tank / (length * width)).toInt()
- Fügen Sie in der Funktion
buildAquarium()
einen Aufruf hinzu, um mit dem neuen sekundären Konstruktor einAquarium
zu erstellen. Geben Sie die Größe und das Volumen an.
fun buildAquarium() {
val aquarium6 = Aquarium(numberOfFish = 29)
aquarium6.printSize()
println("Volume: ${aquarium6.width * aquarium6.length * aquarium6.height / 1000} l")
}
- Führen Sie das Programm aus und beobachten Sie die Ausgabe.
⇒ aquarium initializing Volume: 80 l Width: 20 cm Length: 100 cm Height: 31 cm Volume: 62 l
Beachten Sie, dass die Lautstärke zweimal ausgegeben wird: einmal vom init
-Block im primären Konstruktor, bevor der sekundäre Konstruktor ausgeführt wird, und einmal vom Code in buildAquarium()
.
Sie hätten das Keyword constructor
auch in den primären Konstruktor aufnehmen können, aber in den meisten Fällen ist das nicht erforderlich.
Schritt 4: Neuen Property-Getter hinzufügen
In diesem Schritt fügen Sie einen expliziten Property-Getter hinzu. In Kotlin werden Getter und Setter automatisch definiert, wenn Sie Attribute definieren. Manchmal muss der Wert für ein Attribut jedoch angepasst oder berechnet werden. Im Beispiel oben haben Sie das Volumen von Aquarium
ausgegeben. Sie können das Volume als Attribut verfügbar machen, indem Sie eine Variable und einen Getter dafür definieren. Da volume
berechnet werden muss, muss der Getter den berechneten Wert zurückgeben. Das können Sie mit einer einzeiligen Funktion tun.
- Definieren Sie in der Klasse
Aquarium
eineInt
-Property mit dem Namenvolume
und eineget()
-Methode, die in der nächsten Zeile das Volumen berechnet.
val volume: Int
get() = width * height * length / 1000 // 1000 cm^3 = 1 l
- Entfernen Sie den
init
-Block, mit dem das Volume ausgegeben wird. - Entfernen Sie den Code in
buildAquarium()
, der das Volume ausgibt. - Fügen Sie in der Methode
printSize()
eine Zeile hinzu, um das Volume auszugeben.
fun printSize() {
println("Width: $width cm " +
"Length: $length cm " +
"Height: $height cm "
)
// 1 l = 1000 cm^3
println("Volume: $volume l")
}
- Führen Sie das Programm aus und beobachten Sie die Ausgabe.
⇒ aquarium initializing Width: 20 cm Length: 100 cm Height: 31 cm Volume: 62 l
Die Dimensionen und das Volumen sind dieselben wie zuvor, aber das Volumen wird nur einmal ausgegeben, nachdem das Objekt sowohl vom primären als auch vom sekundären Konstruktor vollständig initialisiert wurde.
Schritt 5: Property-Setter hinzufügen
In diesem Schritt erstellen Sie einen neuen Property-Setter für das Volume.
- Ändern Sie in der Klasse
Aquarium
volume
invar
, damit sie mehr als einmal festgelegt werden kann. - Fügen Sie einen Setter für die
volume
-Eigenschaft hinzu, indem Sie unter dem Getter eineset()
-Methode hinzufügen, die die Höhe basierend auf der angegebenen Wassermenge neu berechnet. Der Name des Setter-Parameters lautet üblicherweisevalue
. Sie können ihn aber auch ändern.
var volume: Int
get() = width * height * length / 1000
set(value) {
height = (value * 1000) / (width * length)
}
- Fügen Sie in
buildAquarium()
Code hinzu, um das Volumen des Aquariums auf 70 Liter festzulegen. Drucken Sie das Bild in der neuen Größe.
fun buildAquarium() {
val aquarium6 = Aquarium(numberOfFish = 29)
aquarium6.printSize()
aquarium6.volume = 70
aquarium6.printSize()
}
- Führen Sie das Programm noch einmal aus und beobachten Sie die geänderte Höhe und Lautstärke.
⇒ aquarium initialized
Width: 20 cm Length: 100 cm Height: 31 cm
Volume: 62 l
Width: 20 cm Length: 100 cm Height: 35 cm
Volume: 70 l
Bisher gab es im Code keine Sichtbarkeitsmodifikatoren wie public
oder private
. Das liegt daran, dass in Kotlin standardmäßig alles öffentlich ist. Das bedeutet, dass auf alles überall zugegriffen werden kann, einschließlich Klassen, Methoden, Eigenschaften und Membervariablen.
In Kotlin können Klassen, Objekte, Schnittstellen, Konstruktoren, Funktionen, Attribute und ihre Setter Sichtbarkeitsmodifizierer haben:
public
bedeutet, dass die Variable außerhalb der Klasse sichtbar ist. Standardmäßig ist alles öffentlich, einschließlich Variablen und Methoden der Klasse.internal
bedeutet, dass es nur in diesem Modul sichtbar ist. Ein Modul ist eine Gruppe von Kotlin-Dateien, die zusammen kompiliert werden, z. B. eine Bibliothek oder Anwendung.private
bedeutet, dass sie nur in dieser Klasse (oder Quelldatei, wenn Sie mit Funktionen arbeiten) sichtbar ist.protected
ist dasselbe wieprivate
, ist aber auch für alle abgeleiteten Klassen sichtbar.
Weitere Informationen finden Sie in der Kotlin-Dokumentation unter Sichtbarkeitsmodifizierer.
Member-Variablen
Attribute innerhalb einer Klasse oder Mitgliedsvariablen sind standardmäßig public
. Wenn Sie sie mit var
definieren, sind sie veränderbar, d. h. lesbar und schreibbar. Wenn Sie sie mit val
definieren, sind sie nach der Initialisierung schreibgeschützt.
Wenn Sie eine Eigenschaft benötigen, die Ihr Code lesen oder schreiben kann, auf die aber nur von außen zugegriffen werden kann, können Sie die Eigenschaft und ihren Getter als öffentlich deklarieren und den Setter als privat, wie unten dargestellt.
var volume: Int
get() = width * height * length / 1000
private set(value) {
height = (value * 1000) / (width * length)
}
In dieser Aufgabe erfahren Sie, wie Unterklassen und Vererbung in Kotlin funktionieren. Sie ähneln den in anderen Sprachen verfügbaren Funktionen, es gibt aber einige Unterschiede.
In Kotlin können Klassen standardmäßig nicht als abgeleitete Klasse verwendet werden. Ebenso können Eigenschaften und Mitgliedsvariablen nicht von Unterklassen überschrieben werden (sie können jedoch aufgerufen werden).
Sie müssen eine Klasse als open
markieren, damit sie abgeleitet werden kann. Ebenso müssen Sie Attribute und Membervariablen als open
markieren, damit sie in der abgeleiteten Klasse überschrieben werden können. Das Schlüsselwort open
ist erforderlich, um zu verhindern, dass Implementierungsdetails versehentlich als Teil der Klassenschnittstelle offengelegt werden.
Schritt 1: Aquarium-Klasse öffnen
In diesem Schritt machen Sie die Klasse Aquarium
zu open
, damit Sie sie im nächsten Schritt überschreiben können.
- Kennzeichnen Sie die Klasse
Aquarium
und alle zugehörigen Attribute mit dem Schlüsselwortopen
.
open class Aquarium (open var length: Int = 100, open var width: Int = 20, open var height: Int = 40) {
open var volume: Int
get() = width * height * length / 1000
set(value) {
height = (value * 1000) / (width * length)
}
- Fügen Sie ein offenes
shape
-Attribut mit dem Wert"rectangle"
hinzu.
open val shape = "rectangle"
- Fügen Sie eine offene
water
-Eigenschaft mit einer Getter-Methode hinzu, die 90% des Volumens vonAquarium
zurückgibt.
open var water: Double = 0.0
get() = volume * 0.9
- Fügen Sie der Methode
printSize()
Code hinzu, um die Form und die Wassermenge als Prozentsatz des Volumens auszugeben.
fun printSize() {
println(shape)
println("Width: $width cm " +
"Length: $length cm " +
"Height: $height cm ")
// 1 l = 1000 cm^3
println("Volume: $volume l Water: $water l (${water/volume*100.0}% full)")
}
- Ändern Sie in
buildAquarium()
den Code so, dass einAquarium
mitwidth = 25
,length = 25
undheight = 40
erstellt wird.
fun buildAquarium() {
val aquarium6 = Aquarium(length = 25, width = 25, height = 40)
aquarium6.printSize()
}
- Führen Sie das Programm aus und sehen Sie sich die neue Ausgabe an.
⇒ aquarium initializing rectangle Width: 25 cm Length: 25 cm Height: 40 cm Volume: 25 l Water: 22.5 l (90.0% full)
Schritt 2: Unterklasse erstellen
- Erstellen Sie eine Unterklasse von
Aquarium
namensTowerTank
, die anstelle eines rechteckigen Tanks einen Tank mit abgerundeten Zylindern implementiert. Sie könnenTowerTank
unterAquarium
hinzufügen, da Sie in derselben Datei wie die KlasseAquarium
eine weitere Klasse hinzufügen können. - Überschreiben Sie in
TowerTank
das Attributheight
, das im Konstruktor definiert ist. Verwenden Sie das Schlüsselwortoverride
in der abgeleiteten Klasse, um eine Property zu überschreiben.
- Lassen Sie den Konstruktor für
TowerTank
eindiameter
annehmen. Verwenden Siediameter
sowohl fürlength
als auch fürwidth
, wenn Sie den Konstruktor in derAquarium
-Superklasse aufrufen.
class TowerTank (override var height: Int, var diameter: Int): Aquarium(height = height, width = diameter, length = diameter) {
- Überschreiben Sie die Volume-Eigenschaft, um einen Zylinder zu berechnen. Die Formel für einen Zylinder lautet: Pi mal Radius zum Quadrat mal Höhe. Sie müssen die Konstante
PI
ausjava.lang.Math
importieren.
override var volume: Int
// ellipse area = π * r1 * r2
get() = (width/2 * length/2 * height / 1000 * PI).toInt()
set(value) {
height = ((value * 1000 / PI) / (width/2 * length/2)).toInt()
}
- Überschreiben Sie in
TowerTank
das Attributwater
mit 80% der Lautstärke.
override var water = volume * 0.8
- Überschreibt die
shape
mit"cylinder"
.
override val shape = "cylinder"
- Ihre endgültige
TowerTank
-Klasse sollte in etwa so aussehen:
Aquarium.kt
:
package example.myapp
import java.lang.Math.PI
... // existing Aquarium class
class TowerTank (override var height: Int, var diameter: Int): Aquarium(height = height, width = diameter, length = diameter) {
override var volume: Int
// ellipse area = π * r1 * r2
get() = (width/2 * length/2 * height / 1000 * PI).toInt()
set(value) {
height = ((value * 1000 / PI) / (width/2 * length/2)).toInt()
}
override var water = volume * 0.8
override val shape = "cylinder"
}
- Erstelle in
buildAquarium()
einTowerTank
mit einem Durchmesser von 25 cm und einer Höhe von 45 cm. Geben Sie die Größe an.
main.kt:
package example.myapp
fun buildAquarium() {
val myAquarium = Aquarium(width = 25, length = 25, height = 40)
myAquarium.printSize()
val myTower = TowerTank(diameter = 25, height = 40)
myTower.printSize()
}
- Führen Sie das Programm aus und beobachten Sie die Ausgabe.
⇒ aquarium initializing rectangle Width: 25 cm Length: 25 cm Height: 40 cm Volume: 25 l Water: 22.5 l (90.0% full) aquarium initializing cylinder Width: 25 cm Length: 25 cm Height: 40 cm Volume: 18 l Water: 14.4 l (80.0% full)
Manchmal möchten Sie gemeinsames Verhalten oder gemeinsame Eigenschaften definieren, die von einigen zugehörigen Klassen gemeinsam genutzt werden sollen. Dafür gibt es in Kotlin zwei Möglichkeiten: Schnittstellen und abstrakte Klassen. In dieser Aufgabe erstellen Sie eine abstrakte Klasse AquariumFish
für Eigenschaften, die allen Fischen gemeinsam sind. Sie erstellen eine Schnittstelle namens FishAction
, um das Verhalten zu definieren, das allen Fischen gemeinsam ist.
- Weder eine abstrakte Klasse noch eine Schnittstelle können eigenständig instanziiert werden. Das bedeutet, dass Sie keine Objekte dieser Typen direkt erstellen können.
- Abstrakte Klassen haben Konstruktoren.
- Schnittstellen dürfen keine Konstruktorlogik enthalten und keinen Status speichern.
Schritt 1: Abstrakte Klasse erstellen
- Erstellen Sie unter example.myapp eine neue Datei mit dem Namen
AquariumFish.kt
. - Erstellen Sie eine Klasse mit dem Namen
AquariumFish
und markieren Sie sie mitabstract
. - Fügen Sie eine
String
-Property (color
) hinzu und kennzeichnen Sie sie mitabstract
.
package example.myapp
abstract class AquariumFish {
abstract val color: String
}
- Erstellen Sie zwei Unterklassen von
AquariumFish
,Shark
undPlecostomus
. - Da
color
abstrakt ist, müssen die abgeleiteten Klassen sie implementieren. Stellen SieShark
auf Grau undPlecostomus
auf Gold ein.
class Shark: AquariumFish() {
override val color = "gray"
}
class Plecostomus: AquariumFish() {
override val color = "gold"
}
- Erstellen Sie in main.kt eine
makeFish()
-Funktion, um Ihre Klassen zu testen. Instanziieren Sie einShark
und einPlecostomus
und geben Sie dann die Farbe der einzelnen Objekte aus. - Löschen Sie den vorherigen Testcode in
main()
und fügen Sie einen Aufruf vonmakeFish()
hinzu. Ihr Code sollte in etwa so aussehen:
main.kt
:
package example.myapp
fun makeFish() {
val shark = Shark()
val pleco = Plecostomus()
println("Shark: ${shark.color}")
println("Plecostomus: ${pleco.color}")
}
fun main () {
makeFish()
}
- Führen Sie das Programm aus und beobachten Sie die Ausgabe.
⇒ Shark: gray Plecostomus: gold
Das folgende Diagramm zeigt die Klassen Shark
und Plecostomus
, die von der abstrakten Klasse AquariumFish
abgeleitet werden.
Schritt 2: Schnittstelle erstellen
- Erstellen Sie in AquariumFish.kt eine Schnittstelle namens
FishAction
mit einer Methodeeat()
.
interface FishAction {
fun eat()
}
- Fügen Sie jeder der Unterklassen
FishAction
hinzu und implementieren Sieeat()
so, dass ausgegeben wird, was der Fisch tut.
class Shark: AquariumFish(), FishAction {
override val color = "gray"
override fun eat() {
println("hunt and eat fish")
}
}
class Plecostomus: AquariumFish(), FishAction {
override val color = "gold"
override fun eat() {
println("eat algae")
}
}
- Lassen Sie in der Funktion
makeFish()
jeden erstellten Fisch etwas fressen, indem Sieeat()
aufrufen.
fun makeFish() {
val shark = Shark()
val pleco = Plecostomus()
println("Shark: ${shark.color}")
shark.eat()
println("Plecostomus: ${pleco.color}")
pleco.eat()
}
- Führen Sie das Programm aus und beobachten Sie die Ausgabe.
⇒ Shark: gray hunt and eat fish Plecostomus: gold eat algae
Das folgende Diagramm zeigt die Klasse Shark
und die Klasse Plecostomus
, die beide aus der Schnittstelle FishAction
bestehen und diese implementieren.
Wann sollten abstrakte Klassen und wann Schnittstellen verwendet werden?
Die obigen Beispiele sind einfach. Wenn Sie jedoch viele zusammenhängende Klassen haben, können abstrakte Klassen und Schnittstellen Ihnen helfen, Ihr Design übersichtlicher, besser organisiert und einfacher zu verwalten.
Wie oben erwähnt, können abstrakte Klassen Konstruktoren haben, Schnittstellen jedoch nicht. Ansonsten sind sie sich sehr ähnlich. Wann sollten Sie die einzelnen Funktionen verwenden?
Wenn Sie Schnittstellen zum Erstellen einer Klasse verwenden, wird die Funktionalität der Klasse durch die darin enthaltenen Klasseninstanzen erweitert. Durch Komposition lässt sich Code in der Regel leichter wiederverwenden und nachvollziehen als durch die Vererbung von einer abstrakten Klasse. Sie können auch mehrere Schnittstellen in einer Klasse verwenden, aber nur von einer abstrakten Klasse ableiten.
Die Komposition führt oft zu einer besseren Kapselung, einer geringeren Kopplung (Abhängigkeit), übersichtlicheren Schnittstellen und einem besser nutzbaren Code. Aus diesen Gründen ist die Verwendung von Komposition mit Schnittstellen das bevorzugte Design. Andererseits ist die Vererbung von einer abstrakten Klasse für einige Probleme oft eine natürliche Lösung. Sie sollten also die Komposition bevorzugen. Wenn die Vererbung jedoch sinnvoll ist, können Sie sie in Kotlin auch verwenden.
- Verwenden Sie eine Schnittstelle, wenn Sie viele Methoden und ein oder zwei Standardimplementierungen haben, z. B. wie in
AquariumAction
unten.
interface AquariumAction {
fun eat()
fun jump()
fun clean()
fun catchFish()
fun swim() {
println("swim")
}
}
- Verwenden Sie eine abstrakte Klasse, wenn Sie eine Klasse nicht vollständig implementieren können. Wenn wir beispielsweise zur Klasse
AquariumFish
zurückkehren, können wir alleAquariumFish
FishAction
implementieren lassen und eine Standardimplementierung füreat
bereitstellen, währendcolor
abstrakt bleibt, da es keine Standardfarbe für Fische gibt.
interface FishAction {
fun eat()
}
abstract class AquariumFish: FishAction {
abstract val color: String
override fun eat() = println("yum")
}
In der vorherigen Aufgabe wurden abstrakte Klassen, Schnittstellen und das Konzept der Komposition eingeführt. Schnittstellendelegierung ist eine fortgeschrittene Technik, bei der die Methoden einer Schnittstelle von einem Hilfs- oder Delegatobjekt implementiert werden, das dann von einer Klasse verwendet wird. Diese Technik kann nützlich sein, wenn Sie eine Schnittstelle in einer Reihe von unabhängigen Klassen verwenden: Sie fügen die benötigte Schnittstellenfunktionalität einer separaten Hilfsklasse hinzu und jede der Klassen verwendet eine Instanz der Hilfsklasse, um die Funktionalität zu implementieren.
In dieser Aufgabe verwenden Sie die Schnittstellendelegierung, um einer Klasse Funktionen hinzuzufügen.
Schritt 1: Neue Schnittstelle erstellen
- Entfernen Sie in AquariumFish.kt die Klasse
AquariumFish
. Anstatt von der KlasseAquariumFish
zu erben, implementierenPlecostomus
undShark
Schnittstellen für die Fischaktion und ihre Farbe. - Erstellen Sie eine neue Schnittstelle,
FishColor
, die die Farbe als String definiert.
interface FishColor {
val color: String
}
- Ändern Sie
Plecostomus
, um zwei Schnittstellen,FishAction
, und eineFishColor
zu implementieren. Sie müssencolor
ausFishColor
undeat()
ausFishAction
überschreiben.
class Plecostomus: FishAction, FishColor {
override val color = "gold"
override fun eat() {
println("eat algae")
}
}
- Ändern Sie Ihre
Shark
-Klasse so, dass sie auch die beiden SchnittstellenFishAction
undFishColor
implementiert, anstatt vonAquariumFish
zu erben.
class Shark: FishAction, FishColor {
override val color = "gray"
override fun eat() {
println("hunt and eat fish")
}
}
- Der fertige Code sollte in etwa so aussehen:
package example.myapp
interface FishAction {
fun eat()
}
interface FishColor {
val color: String
}
class Plecostomus: FishAction, FishColor {
override val color = "gold"
override fun eat() {
println("eat algae")
}
}
class Shark: FishAction, FishColor {
override val color = "gray"
override fun eat() {
println("hunt and eat fish")
}
}
Schritt 2: Singleton-Klasse erstellen
Als Nächstes implementieren Sie die Einrichtung für den Delegierungsteil, indem Sie eine Hilfsklasse erstellen, die FishColor
implementiert. Sie erstellen eine einfache Klasse namens GoldColor
, die FishColor
implementiert. Sie gibt lediglich an, dass die Farbe Gold ist.
Es ist nicht sinnvoll, mehrere Instanzen von GoldColor
zu erstellen, da sie alle genau dasselbe tun würden. In Kotlin können Sie eine Klasse deklarieren, von der nur eine Instanz erstellt werden kann. Verwenden Sie dazu das Schlüsselwort object
anstelle von class
. Kotlin erstellt diese eine Instanz und auf diese Instanz wird über den Klassennamen verwiesen. Alle anderen Objekte können dann einfach diese eine Instanz verwenden. Es gibt keine Möglichkeit, andere Instanzen dieser Klasse zu erstellen. Wenn Sie mit dem Singleton-Muster vertraut sind, wissen Sie, wie Sie Singletons in Kotlin implementieren.
- Erstellen Sie in AquariumFish.kt ein Objekt für
GoldColor
. Überschreiben Sie die Farbe.
object GoldColor : FishColor {
override val color = "gold"
}
Schritt 3: Schnittstellendelegierung für FishColor hinzufügen
Jetzt können Sie die Schnittstellendelegierung verwenden.
- Entfernen Sie in AquariumFish.kt die Überschreibung von
color
ausPlecostomus
. - Ändern Sie die
Plecostomus
-Klasse, damit ihre Farbe vonGoldColor
übernommen wird. Fügen Sie dazu der Klassendeklarationby GoldColor
hinzu, um die Delegation zu erstellen. Das bedeutet, dass Sie anstelle vonFishColor
die vonGoldColor
bereitgestellte Implementierung verwenden sollten. Jedes Mal, wenn aufcolor
zugegriffen wird, wird der Zugriff anGoldColor
delegiert.
class Plecostomus: FishAction, FishColor by GoldColor {
override fun eat() {
println("eat algae")
}
}
In der aktuellen Version der Klasse sind alle Antennenwelse goldfarben. Diese Fische gibt es aber in vielen Farben. Sie können dieses Problem beheben, indem Sie einen Konstruktorparameter für die Farbe mit GoldColor
als Standardfarbe für Plecostomus
hinzufügen.
- Ändern Sie die Klasse
Plecostomus
so, dass sie ein übergebenesfishColor
mit ihrem Konstruktor akzeptiert, und legen Sie den Standardwert aufGoldColor
fest. Ändern Sie die Delegation vonby GoldColor
inby fishColor
.
class Plecostomus(fishColor: FishColor = GoldColor): FishAction,
FishColor by fishColor {
override fun eat() {
println("eat algae")
}
}
Schritt 4: Schnittstellendelegierung für FishAction hinzufügen
Auf dieselbe Weise können Sie die Schnittstellendelegierung für FishAction
verwenden.
- Erstelle in AquariumFish.kt eine
PrintingFishAction
-Klasse, dieFishAction
implementiert und einenString
und einenfood
akzeptiert. Gib dann aus, was der Fisch frisst.
class PrintingFishAction(val food: String) : FishAction {
override fun eat() {
println(food)
}
}
- Entfernen Sie in der Klasse
Plecostomus
die Überschreibungsfunktioneat()
, da Sie sie durch eine Delegation ersetzen. - Delegieren Sie in der Deklaration von
Plecostomus
den ParameterFishAction
anPrintingFishAction
und übergeben Sie"eat algae"
. - Da alles delegiert wird, gibt es keinen Code im Hauptteil der
Plecostomus
-Klasse. Entfernen Sie daher{}
, da alle Überschreibungen durch die Schnittstellendelegierung verarbeitet werden.
class Plecostomus (fishColor: FishColor = GoldColor):
FishAction by PrintingFishAction("eat algae"),
FishColor by fishColor
Im folgenden Diagramm werden die Klassen Shark
und Plecostomus
dargestellt, die beide aus den Schnittstellen PrintingFishAction
und FishColor
bestehen, aber die Implementierung an diese delegieren.
Die Schnittstellendelegierung ist leistungsstark und Sie sollten sie immer dann in Betracht ziehen, wenn Sie in einer anderen Sprache eine abstrakte Klasse verwenden würden. So können Sie Verhalten über Komposition einfügen, anstatt viele Unterklassen zu benötigen, die jeweils auf unterschiedliche Weise spezialisiert sind.
Eine Datenklasse ähnelt in einigen anderen Sprachen einer struct
– sie dient hauptsächlich zum Speichern von Daten –, aber ein Datenklassenobjekt ist trotzdem ein Objekt. Kotlin-Datenklassenobjekte bieten einige zusätzliche Vorteile, z. B. Dienstprogramme zum Drucken und Kopieren. In dieser Aufgabe erstellen Sie eine einfache Datenklasse und erfahren, wie Kotlin Datenklassen unterstützt.
Schritt 1: Datenklasse erstellen
- Fügen Sie unter dem Paket example.myapp ein neues Paket
decor
hinzu, um den neuen Code zu speichern. Klicken Sie im Bereich Project (Projekt) mit der rechten Maustaste auf example.myapp und wählen Sie File > New > Package (Datei > Neu > Paket) aus. - Erstellen Sie im Paket eine neue Klasse mit dem Namen
Decoration
.
package example.myapp.decor
class Decoration {
}
- Wenn Sie
Decoration
zu einer Datenklasse machen möchten, stellen Sie der Klassendeklaration das Schlüsselwortdata
voran. - Fügen Sie eine
String
-Property mit dem Namenrocks
hinzu, um der Klasse einige Daten zu geben.
data class Decoration(val rocks: String) {
}
- Fügen Sie in der Datei außerhalb der Klasse eine
makeDecorations()
-Funktion hinzu, um eine Instanz vonDecoration
mit"granite"
zu erstellen und auszugeben.
fun makeDecorations() {
val decoration1 = Decoration("granite")
println(decoration1)
}
- Fügen Sie eine
main()
-Funktion hinzu, ummakeDecorations()
aufzurufen, und führen Sie das Programm aus. Beachten Sie die sinnvolle Ausgabe, die erstellt wird, da es sich um eine Datenklasse handelt.
⇒ Decoration(rocks=granite)
- Instanziieren Sie in
makeDecorations()
zwei weitereDecoration
-Objekte, die beide „slate“ sind, und geben Sie sie aus.
fun makeDecorations() {
val decoration1 = Decoration("granite")
println(decoration1)
val decoration2 = Decoration("slate")
println(decoration2)
val decoration3 = Decoration("slate")
println(decoration3)
}
- Fügen Sie in
makeDecorations()
eine Print-Anweisung hinzu, die das Ergebnis des Vergleichs vondecoration1
mitdecoration2
ausgibt, und eine zweite, diedecoration3
mitdecoration2
vergleicht. Verwenden Sie die von Datenklassen bereitgestellte Methode equals().
println (decoration1.equals(decoration2))
println (decoration3.equals(decoration2))
- Führen Sie den Code aus.
⇒ Decoration(rocks=granite) Decoration(rocks=slate) Decoration(rocks=slate) false true
Schritt 2: Destrukturierung verwenden
Um auf die Eigenschaften eines Datenobjekts zuzugreifen und sie Variablen zuzuweisen, können Sie sie einzeln zuweisen, wie hier gezeigt.
val rock = decoration.rock
val wood = decoration.wood
val diver = decoration.diver
Stattdessen können Sie für jede Property eine Variable erstellen und das Datenobjekt der Gruppe von Variablen zuweisen. In Kotlin wird der Attributwert in jeder Variablen gespeichert.
val (rock, wood, diver) = decoration
Das wird als Destrukturierung bezeichnet und ist eine nützliche Abkürzung. Die Anzahl der Variablen muss mit der Anzahl der Attribute übereinstimmen und die Variablen werden in der Reihenfolge zugewiesen, in der sie in der Klasse deklariert werden. Hier ist ein vollständiges Beispiel, das Sie in Decoration.kt ausprobieren können.
// Here is a data class with 3 properties.
data class Decoration2(val rocks: String, val wood: String, val diver: String){
}
fun makeDecorations() {
val d5 = Decoration2("crystal", "wood", "diver")
println(d5)
// Assign all properties to variables.
val (rock, wood, diver) = d5
println(rock)
println(wood)
println(diver)
}
⇒ Decoration2(rocks=crystal, wood=wood, diver=diver) crystal wood diver
Wenn Sie eine oder mehrere der Eigenschaften nicht benötigen, können Sie sie überspringen, indem Sie _
anstelle eines Variablennamens verwenden, wie im folgenden Code gezeigt.
val (rock, _, diver) = d5
In dieser Aufgabe lernen Sie einige der speziellen Klassen in Kotlin kennen, darunter die folgenden:
- Singleton-Klassen
- Enums
- Versiegelte Klassen
Schritt 1: Singleton-Klassen aufrufen
Erinnern Sie sich an das Beispiel von vorhin mit der Klasse GoldColor
.
object GoldColor : FishColor {
override val color = "gold"
}
Da jede Instanz von GoldColor
dasselbe tut, wird sie als object
anstelle von class
deklariert, um sie zu einem Singleton zu machen. Es kann nur eine Instanz davon geben.
Schritt 2: Enum erstellen
Kotlin unterstützt auch Enums, mit denen Sie etwas aufzählen und anhand des Namens darauf verweisen können, ähnlich wie in anderen Sprachen. Deklarieren Sie ein Enum, indem Sie der Deklaration das Keyword enum
voranstellen. Für eine einfache Enum-Deklaration ist nur eine Liste von Namen erforderlich. Sie können aber auch ein oder mehrere Felder definieren, die mit jedem Namen verknüpft sind.
- Sehen Sie sich in Decoration.kt ein Beispiel für ein Enum an.
enum class Color(val rgb: Int) {
RED(0xFF0000), GREEN(0x00FF00), BLUE(0x0000FF);
}
Enums ähneln Singletons – es kann nur einen und nur einen Wert für jeden Wert in der Enumeration geben. Es kann beispielsweise nur ein Color.RED
, ein Color.GREEN
und ein Color.BLUE
geben. In diesem Beispiel werden die RGB-Werte der Eigenschaft rgb
zugewiesen, um die Farbkomponenten darzustellen. Sie können auch den Ordinalwert eines Enums mit der Property ordinal
und seinen Namen mit der Property name
abrufen.
- Sehen wir uns ein weiteres Beispiel für eine Enumeration an.
enum class Direction(val degrees: Int) {
NORTH(0), SOUTH(180), EAST(90), WEST(270)
}
fun main() {
println(Direction.EAST.name)
println(Direction.EAST.ordinal)
println(Direction.EAST.degrees)
}
⇒ EAST 2 90
Schritt 3: Versiegelte Klasse erstellen
Eine versiegelte Klasse ist eine Klasse, die abgeleitet werden kann, aber nur in der Datei, in der sie deklariert ist. Wenn Sie versuchen, die Klasse in einer anderen Datei unterzuordnen, erhalten Sie eine Fehlermeldung.
Da sich die Klassen und Unterklassen in derselben Datei befinden, kennt Kotlin alle Unterklassen statisch. Das heißt, zur Kompilierzeit sieht der Compiler alle Klassen und Unterklassen und weiß, dass dies alle sind. Daher kann er zusätzliche Prüfungen für Sie durchführen.
- Sehen Sie sich in AquariumFish.kt ein Beispiel für eine versiegelte Klasse an, die zum Thema Wasser passt.
sealed class Seal
class SeaLion : Seal()
class Walrus : Seal()
fun matchSeal(seal: Seal): String {
return when(seal) {
is Walrus -> "walrus"
is SeaLion -> "sea lion"
}
}
Die Klasse Seal
kann nicht in einer anderen Datei abgeleitet werden. Wenn Sie weitere Seal
-Typen hinzufügen möchten, müssen Sie sie in derselben Datei hinzufügen. Versiegelte Klassen sind daher eine sichere Möglichkeit, eine feste Anzahl von Typen darzustellen. Versiegelte Klassen eignen sich beispielsweise hervorragend, um Erfolg oder Fehler von einer Netzwerk-API zurückzugeben.
In dieser Lektion haben wir viele Themen behandelt. Vieles davon sollte Ihnen aus anderen objektorientierten Programmiersprachen bekannt sein. Kotlin bietet jedoch einige zusätzliche Funktionen, um Code prägnant und lesbar zu halten.
Klassen und Konstruktoren
- Definieren Sie eine Klasse in Kotlin mit
class
. - In Kotlin werden automatisch Setter und Getter für Eigenschaften erstellt.
- Definieren Sie den primären Konstruktor direkt in der Klassendefinition. Beispiel:
class Aquarium(var length: Int = 100, var width: Int = 20, var height: Int = 40)
- Wenn für einen primären Konstruktor zusätzlicher Code erforderlich ist, schreiben Sie ihn in einen oder mehrere
init
-Blöcke. - In einer Klasse können mit
constructor
ein oder mehrere sekundäre Konstruktoren definiert werden. Es ist jedoch üblich, stattdessen eine Fabrikfunktion zu verwenden.
Sichtbarkeitsmodifizierer und Unterklassen
- Alle Klassen und Funktionen in Kotlin sind standardmäßig
public
, aber Sie können Modifizierer verwenden, um die Sichtbarkeit ininternal
,private
oderprotected
zu ändern. - Damit eine abgeleitete Klasse erstellt werden kann, muss die übergeordnete Klasse mit
open
gekennzeichnet sein. - Wenn Sie Methoden und Attribute in einer Unterklasse überschreiben möchten, müssen die Methoden und Attribute in der übergeordneten Klasse mit
open
gekennzeichnet sein. - Eine versiegelte Klasse kann nur in derselben Datei untergeordnet werden, in der sie definiert ist. Sie können eine versiegelte Klasse erstellen, indem Sie der Deklaration
sealed
voranstellen.
Datenklassen, Singletons und Enums
- Erstellen Sie eine Datenklasse, indem Sie der Deklaration
data
voranstellen. - Destrukturierung ist eine Kurzform für die Zuweisung der Eigenschaften eines
data
-Objekts zu separaten Variablen. - Erstellen Sie eine Singleton-Klasse, indem Sie
object
anstelle vonclass
verwenden. - Definieren Sie eine Aufzählung mit
enum class
.
Abstrakte Klassen, Schnittstellen und Delegierung
- Abstrakte Klassen und Schnittstellen sind zwei Möglichkeiten, gemeinsames Verhalten zwischen Klassen zu teilen.
- Eine abstrakte Klasse definiert Eigenschaften und Verhalten, überlässt die Implementierung jedoch den abgeleiteten Klassen.
- Ein Interface definiert das Verhalten und kann Standardimplementierungen für einen Teil oder das gesamte Verhalten bereitstellen.
- Wenn Sie Schnittstellen zum Erstellen einer Klasse verwenden, wird die Funktionalität der Klasse durch die darin enthaltenen Klasseninstanzen erweitert.
- Bei der Schnittstellendelegierung wird die Komposition verwendet, aber die Implementierung wird auch an die Schnittstellenklassen delegiert.
- Die Komposition ist eine leistungsstarke Methode, um einer Klasse mithilfe der Schnittstellendelegation Funktionen hinzuzufügen. Im Allgemeinen wird die Komposition bevorzugt, aber die Vererbung von einer abstrakten Klasse ist für einige Probleme besser geeignet.
Kotlin-Dokumentation
Wenn Sie weitere Informationen zu einem Thema in diesem Kurs benötigen oder nicht weiterkommen, ist https://kotlinlang.org der beste Ausgangspunkt.
- Klassen und Vererbung
- Konstruktoren
- Factory-Funktionen
- Attribute und Felder
- Sichtbarkeitsmodifikatoren
- Abstrakte Klassen
- Schnittstellen
- Delegation
- Datenklassen
- Equality
- Destrukturierung
- Objektdeklarationen
- Enum-Klassen
- Sealed Classes
- Optionale Fehler mit versiegelten Klassen in Kotlin behandeln
Kotlin-Tutorials
Die Website https://try.kotlinlang.org enthält umfangreiche Tutorials namens „Kotlin Koans“, einen webbasierten Interpreter und eine vollständige Referenzdokumentation mit Beispielen.
Udacity-Kurs
Den Udacity-Kurs zu diesem Thema finden Sie unter Kotlin Bootcamp for Programmers.
IntelliJ IDEA
Dokumentation für IntelliJ IDEA finden Sie auf der JetBrains-Website.
In diesem Abschnitt werden mögliche Hausaufgaben für Schüler und Studenten aufgeführt, die dieses Codelab im Rahmen eines von einem Kursleiter geleiteten Kurses durcharbeiten. Es liegt in der Verantwortung des Kursleiters, Folgendes zu tun:
- Weisen Sie bei Bedarf Aufgaben zu.
- Teilen Sie den Schülern/Studenten mit, wie sie Hausaufgaben abgeben können.
- Benoten Sie die Hausaufgaben.
Lehrkräfte können diese Vorschläge nach Belieben nutzen und auch andere Hausaufgaben zuweisen, die sie für angemessen halten.
Wenn Sie dieses Codelab selbst durcharbeiten, können Sie mit diesen Hausaufgaben Ihr Wissen testen.
Beantworten Sie diese Fragen
Frage 1
Klassen haben eine spezielle Methode, die als Vorlage für das Erstellen von Objekten dieser Klasse dient. Wie heißt die Methode?
▢ Ein Builder
▢ Ein Instanziator
▢ Ein Konstruktor
▢ Ein Blueprint
Frage 2
Welche der folgenden Aussagen zu Schnittstellen und abstrakten Klassen ist NICHT richtig?
▢ Abstrakte Klassen können Konstruktoren haben.
▢ Schnittstellen dürfen keine Konstruktoren haben.
▢ Schnittstellen und abstrakte Klassen können direkt instanziiert werden.
▢ Abstrakte Properties müssen von abgeleiteten Klassen der abstrakten Klasse implementiert werden.
Frage 3
Welche der folgenden Optionen ist KEIN Kotlin-Sichtbarkeitsmodifizierer für Attribute, Methoden usw.?
▢ internal
▢ nosubclass
▢ protected
▢ private
Frage 4
Betrachten Sie diese Datenklasse:data class Fish(val name: String, val species:String, val colors:String)
Welcher der folgenden Codes ist NICHT gültig, um ein Fish
-Objekt zu erstellen und zu dekonstruieren?
▢ val (name1, species1, colors1) = Fish("Pat", "Plecostomus", "gold")
▢ val (name2, _, colors2) = Fish("Bitey", "shark", "gray")
▢ val (name3, species3, _) = Fish("Amy", "angelfish", "blue and black stripes")
▢ val (name4, species4, colors4) = Fish("Harry", "halibut")
Frage 5
Angenommen, Sie betreiben einen Zoo mit vielen Tieren, die alle versorgt werden müssen. Welche der folgenden Aufgaben gehört NICHT zur Umsetzung der Rolle als „Caretaker“?
▢ Eine interface
für verschiedene Arten von Futter, die Tiere fressen.
▢ Eine abstract Caretaker
-Klasse, aus der Sie verschiedene Arten von Betreuern erstellen können.
▢ Eine interface
für das Geben von sauberem Wasser an ein Tier.
▢ Eine data
-Klasse für einen Eintrag in einem Fütterungsplan.
Fahren Sie mit der nächsten Lektion fort:
Eine Übersicht über den Kurs mit Links zu anderen Codelabs finden Sie unter „Kotlin Bootcamp for Programmers: Welcome to the course“.