Kotlin Bootcamp für Programmierer 5.2: Generics

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 lernen Sie generische Klassen, Funktionen und Methoden kennen und erfahren, wie sie in Kotlin funktionieren.

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 Syntax von Kotlin-Funktionen, -Klassen und -Methoden
  • Neue Klasse in IntelliJ IDEA erstellen und Programm ausführen

Lerninhalte

  • So arbeiten Sie mit generischen Klassen, Methoden und Funktionen

Aufgaben

  • Allgemeine Klasse erstellen und Einschränkungen hinzufügen
  • in- und out-Typen erstellen
  • Generische Funktionen, Methoden und Erweiterungsfunktionen erstellen

Einführung in Generics

Wie viele Programmiersprachen hat auch Kotlin generische Typen. Mit einem generischen Typ können Sie eine Klasse generisch und damit viel flexibler machen.

Stellen Sie sich vor, Sie implementieren eine MyList-Klasse, die eine Liste von Elementen enthält. Ohne Generics müssten Sie für jeden Typ eine neue Version von MyList implementieren: eine für Double, eine für String und eine für Fish. Mit Generics können Sie die Liste generisch machen, sodass sie beliebige Objekttypen enthalten kann. Es ist, als würde man den Typ zu einem Platzhalter machen, der für viele Typen passt.

Um einen generischen Typ zu definieren, setzen Sie T in spitze Klammern <T> nach dem Klassennamen. Sie könnten auch einen anderen Buchstaben oder einen längeren Namen verwenden, aber die Konvention für einen generischen Typ ist „T“.

class MyList<T> {
    fun get(pos: Int): T {
        TODO("implement")
    }
    fun addItem(item: T) {}
}

Sie können auf T verweisen, als wäre es ein normaler Typ. Der Rückgabetyp für get() ist T und der Parameter für addItem() hat den Typ T. Generische Listen sind natürlich sehr nützlich, daher ist die Klasse List in Kotlin integriert.

Schritt 1: Typografiehierarchie erstellen

In diesem Schritt erstellen Sie einige Klassen, die Sie im nächsten Schritt verwenden. Das Erstellen von Unterklassen wurde in einem früheren Codelab behandelt. Hier ist eine kurze Zusammenfassung.

  1. Damit das Beispiel übersichtlich bleibt, erstellen Sie ein neues Paket unter src und nennen Sie es generics.
  2. Erstellen Sie im Paket generics eine neue Aquarium.kt-Datei. So können Sie Dinge mit denselben Namen ohne Konflikte neu definieren. Der restliche Code für dieses Codelab gehört also in diese Datei.
  3. Erstelle eine Typenhierarchie für die verschiedenen Arten der Wasserversorgung. Machen Sie WaterSupply zuerst zu einer open-Klasse, damit sie abgeleitet werden kann.
  4. Fügen Sie einen booleschen var-Parameter hinzu: needsProcessing. Dadurch wird automatisch eine veränderliche Property zusammen mit einer Getter- und einer Setter-Methode erstellt.
  5. Erstelle eine Unterklasse TapWater, die WaterSupply erweitert, und übergib true für needsProcessing, da das Leitungswasser Zusatzstoffe enthält, die für Fische schädlich sind.
  6. Definieren Sie in TapWater eine Funktion namens addChemicalCleaners(), die needsProcessing nach der Reinigung des Wassers auf false setzt. Das Attribut needsProcessing kann ab TapWater festgelegt werden, da es standardmäßig public ist und auf Unterklassen zugegriffen werden kann. Hier ist der vollständige Code.
package generics

open class WaterSupply(var needsProcessing: Boolean)

class TapWater : WaterSupply(true) {
   fun addChemicalCleaners() {
       needsProcessing = false
   }
}
  1. Erstellen Sie zwei weitere Unterklassen von WaterSupply mit den Namen FishStoreWater und LakeWater. FishStoreWater muss nicht verarbeitet werden, LakeWater muss aber mit der Methode filter() gefiltert werden. Nach dem Filtern muss sie nicht noch einmal verarbeitet werden. Legen Sie daher in filter() needsProcessing = false fest.
class FishStoreWater : WaterSupply(false)

class LakeWater : WaterSupply(true) {
   fun filter() {
       needsProcessing = false
   }
}

Weitere Informationen finden Sie in der vorherigen Lektion zur Vererbung in Kotlin.

Schritt 2: Generische Klasse erstellen

In diesem Schritt ändern Sie die Klasse Aquarium, um verschiedene Arten von Wasserversorgung zu unterstützen.

  1. Definieren Sie in Aquarium.kt eine Aquarium-Klasse mit <T> in Klammern nach dem Klassennamen.
  2. Fügen Sie Aquarium die unveränderliche Eigenschaft waterSupply vom Typ T hinzu.
class Aquarium<T>(val waterSupply: T)
  1. Schreibe eine Funktion mit dem Namen genericsExample(). Da dies nicht Teil einer Klasse ist, kann es wie die Funktion main() oder die Klassendefinitionen auf der obersten Ebene der Datei stehen. Erstellen Sie in der Funktion ein Aquarium und übergeben Sie ein WaterSupply. Da der Parameter waterSupply generisch ist, müssen Sie den Typ in spitzen Klammern <> angeben.
fun genericsExample() {
    val aquarium = Aquarium<TapWater>(TapWater())
}
  1. In genericsExample() kann Ihr Code auf die waterSupply des Aquariums zugreifen. Da es vom Typ TapWater ist, können Sie addChemicalCleaners() ohne Typumwandlungen aufrufen.
fun genericsExample() {
    val aquarium = Aquarium<TapWater>(TapWater())
    aquarium.waterSupply.addChemicalCleaners()
}
  1. Beim Erstellen des Aquarium-Objekts können Sie die spitzen Klammern und den Inhalt dazwischen entfernen, da Kotlin eine Typinferenz hat. Es gibt also keinen Grund, TapWater zweimal anzugeben, wenn Sie die Instanz erstellen. Der Typ kann aus dem Argument für Aquarium abgeleitet werden. Es wird trotzdem ein Aquarium vom Typ TapWater erstellt.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    aquarium.waterSupply.addChemicalCleaners()
}
  1. Um zu sehen, was passiert, geben Sie needsProcessing vor und nach dem Aufrufen von addChemicalCleaners() aus. Unten sehen Sie die vollständige Funktion.
fun genericsExample() {
    val aquarium = Aquarium<TapWater>(TapWater())
    println("water needs processing: ${aquarium.waterSupply.needsProcessing}")
    aquarium.waterSupply.addChemicalCleaners()
    println("water needs processing: ${aquarium.waterSupply.needsProcessing}")
}
  1. Fügen Sie eine main()-Funktion hinzu, um genericsExample() aufzurufen, führen Sie dann das Programm aus und sehen Sie sich das Ergebnis an.
fun main() {
    genericsExample()
}
⇒ water needs processing: true
water needs processing: false

Schritt 3: Frage präzisieren

„Generisch“ bedeutet, dass Sie fast alles übergeben können, was manchmal ein Problem ist. In diesem Schritt legen Sie für die Klasse Aquarium genauer fest, was darin enthalten sein kann.

  1. Erstellen Sie in genericsExample() ein Aquarium und übergeben Sie einen String für waterSupply. Geben Sie dann das Attribut waterSupply des Aquariums aus.
fun genericsExample() {
    val aquarium2 = Aquarium("string")
    println(aquarium2.waterSupply)
}
  1. Führen Sie das Programm aus und sehen Sie sich das Ergebnis an.
⇒ string

Das Ergebnis ist der übergebene String, da Aquarium keine Einschränkungen für T.festlegt. Es kann ein beliebiger Typ übergeben werden, einschließlich String.

  1. Erstellen Sie in genericsExample() einen weiteren Aquarium und übergeben Sie null für den waterSupply. Wenn waterSupply null ist, wird "waterSupply is null" ausgegeben.
fun genericsExample() {
    val aquarium3 = Aquarium(null)
    if (aquarium3.waterSupply == null) {
        println("waterSupply is null")
    }
}
  1. Führen Sie das Programm aus und sehen Sie sich das Ergebnis an.
⇒ waterSupply is null

Warum kann ich null übergeben, wenn ich ein Aquarium erstelle? Das ist möglich, weil T standardmäßig für den Typ Any? steht, der Nullwerte zulässt und sich an der Spitze der Typhierarchie befindet. Das Folgende entspricht Ihrer vorherigen Eingabe.

class Aquarium<T: Any?>(val waterSupply: T)
  1. Wenn Sie nicht zulassen möchten, dass null übergeben wird, müssen Sie T explizit vom Typ Any machen, indem Sie ? nach Any entfernen.
class Aquarium<T: Any>(val waterSupply: T)

In diesem Zusammenhang wird Any als generische Einschränkung bezeichnet. Das bedeutet, dass für T jeder Typ übergeben werden kann, solange er nicht null ist.

  1. Sie möchten sicherstellen, dass für T nur ein WaterSupply-Objekt (oder eines seiner untergeordneten Objekte) übergeben werden kann. Ersetzen Sie Any durch WaterSupply, um eine spezifischere generische Einschränkung zu definieren.
class Aquarium<T: WaterSupply>(val waterSupply: T)

Schritt 4: Weitere Prüfungen hinzufügen

In diesem Schritt lernen Sie die Funktion check() kennen, mit der Sie dafür sorgen können, dass sich Ihr Code wie erwartet verhält. Die check()-Funktion ist eine Standardbibliotheksfunktion in Kotlin. Sie fungiert als Zusicherung und löst eine IllegalStateException aus, wenn ihr Argument als false ausgewertet wird.

  1. Fügen Sie der Klasse Aquarium eine addWater()-Methode hinzu, um Wasser hinzuzufügen. Verwenden Sie dabei ein check(), damit das Wasser nicht zuerst verarbeitet werden muss.
class Aquarium<T: WaterSupply>(val waterSupply: T) {
    fun addWater() {
        check(!waterSupply.needsProcessing) { "water supply needs processing first" }
        println("adding water from $waterSupply")
    }    
}

Wenn needsProcessing „true“ ist, löst check() eine Ausnahme aus.

  1. Füge in genericsExample() Code hinzu, um ein Aquarium mit LakeWater zu erstellen, und füge dann etwas Wasser hinzu.
fun genericsExample() {
    val aquarium4 = Aquarium(LakeWater())
    aquarium4.addWater()
}
  1. Wenn Sie das Programm ausführen, erhalten Sie eine Ausnahme, da das Wasser zuerst gefiltert werden muss.
⇒ Exception in thread "main" java.lang.IllegalStateException: water supply needs processing first
        at Aquarium.generics.Aquarium.addWater(Aquarium.kt:21)
  1. Fügen Sie einen Aufruf zum Filtern des Wassers hinzu, bevor Sie es dem Aquarium hinzufügen. Wenn Sie das Programm jetzt ausführen, wird keine Ausnahme ausgelöst.
fun genericsExample() {
    val aquarium4 = Aquarium(LakeWater())
    aquarium4.waterSupply.filter()
    aquarium4.addWater()
}
⇒ adding water from generics.LakeWater@880ec60

Das waren die Grundlagen zu Generics. Die folgenden Aufgaben decken mehr ab, aber das wichtige Konzept ist, wie eine generische Klasse mit einer generischen Einschränkung deklariert und verwendet wird.

In dieser Aufgabe lernen Sie In- und Out-Typen mit Generics kennen. Ein in-Typ ist ein Typ, der nur an eine Klasse übergeben, aber nicht zurückgegeben werden kann. Ein out-Typ ist ein Typ, der nur von einer Klasse zurückgegeben werden kann.

Wenn Sie sich die Klasse Aquarium ansehen, werden Sie feststellen, dass der generische Typ nur beim Abrufen des Attributs waterSupply zurückgegeben wird. Es gibt keine Methoden, die einen Wert vom Typ T als Parameter akzeptieren (außer bei der Definition im Konstruktor). In Kotlin können Sie out-Typen für genau diesen Fall definieren. Außerdem können zusätzliche Informationen darüber abgeleitet werden, wo die Typen sicher verwendet werden können. Ebenso können Sie in-Typen für generische Typen definieren, die nur an Methoden übergeben, aber nicht zurückgegeben werden. Dadurch kann Kotlin zusätzliche Prüfungen für die Codesicherheit durchführen.

Die Typen in und out sind Direktiven für das Typsystem von Kotlin. Das gesamte Typsystem zu erläutern, würde den Rahmen dieses Bootcamps sprengen. Der Compiler kennzeichnet jedoch Typen, die nicht entsprechend mit in und out gekennzeichnet sind.

Schritt 1: Out-Typ definieren

  1. Ändern Sie in der Klasse Aquarium den Typ von T: WaterSupply in out.
class Aquarium<out T: WaterSupply>(val waterSupply: T) {
    ...
}
  1. Deklarieren Sie in derselben Datei außerhalb der Klasse eine Funktion addItemTo(), die eine Aquarium vom Typ WaterSupply erwartet.
fun addItemTo(aquarium: Aquarium<WaterSupply>) = println("item added")
  1. Rufen Sie addItemTo() über genericsExample() auf und führen Sie Ihr Programm aus.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    addItemTo(aquarium)
}
⇒ item added

Kotlin kann dafür sorgen, dass addItemTo() keine typsicheren Aktionen mit dem generischen WaterSupply ausführt, da es als out-Typ deklariert ist.

  1. Wenn Sie das Keyword out entfernen, gibt der Compiler beim Aufrufen von addItemTo() einen Fehler aus, da Kotlin nicht garantieren kann, dass Sie mit dem Typ nichts Unsicheres tun.

Schritt 2: „in“-Typ definieren

Der Typ in ähnelt dem Typ out, jedoch für generische Typen, die nur an Funktionen übergeben, aber nicht zurückgegeben werden. Wenn Sie versuchen, einen Typ in zurückzugeben, erhalten Sie einen Compilerfehler. In diesem Beispiel definieren Sie einen in-Typ als Teil einer Schnittstelle.

  1. Definieren Sie in Aquarium.kt eine Schnittstelle Cleaner, die einen generischen Typ T verwendet, der auf WaterSupply beschränkt ist. Da er nur als Argument für clean() verwendet wird, können Sie ihn zu einem in-Parameter machen.
interface Cleaner<in T: WaterSupply> {
    fun clean(waterSupply: T)
}
  1. Wenn Sie die Cleaner-Schnittstelle verwenden möchten, erstellen Sie eine Klasse TapWaterCleaner, die Cleaner zum Reinigen von TapWater durch Hinzufügen von Chemikalien implementiert.
class TapWaterCleaner : Cleaner<TapWater> {
    override fun clean(waterSupply: TapWater) =   waterSupply.addChemicalCleaners()
}
  1. Aktualisieren Sie in der Klasse Aquarium addWater(), um ein Cleaner vom Typ T zu verwenden, und reinigen Sie das Wasser, bevor Sie es hinzufügen.
class Aquarium<out T: WaterSupply>(val waterSupply: T) {
    fun addWater(cleaner: Cleaner<T>) {
        if (waterSupply.needsProcessing) {
            cleaner.clean(waterSupply)
        }
        println("water added")
    }
}
  1. Aktualisieren Sie den Beispielcode für genericsExample(), um ein TapWaterCleaner und ein Aquarium mit TapWater zu erstellen. Fügen Sie dann mit dem Reiniger etwas Wasser hinzu. Der Reiniger wird nach Bedarf verwendet.
fun genericsExample() {
    val cleaner = TapWaterCleaner()
    val aquarium = Aquarium(TapWater())
    aquarium.addWater(cleaner)
}

Kotlin verwendet die Typinformationen in und out, um sicherzustellen, dass in Ihrem Code Generics sicher verwendet werden. Out und in sind leicht zu merken: out-Typen können als Rückgabewerte nach außen übergeben werden, in-Typen können als Argumente nach innen übergeben werden.

Wenn Sie mehr über die Art von Problemen erfahren möchten, die mit In- und Out-Typen gelöst werden, finden Sie in der Dokumentation ausführliche Informationen.

In dieser Aufgabe erfahren Sie mehr über generische Funktionen und wann Sie sie verwenden sollten. In der Regel ist es eine gute Idee, eine generische Funktion zu erstellen, wenn die Funktion ein Argument einer Klasse mit einem generischen Typ akzeptiert.

Schritt 1: Generische Funktion erstellen

  1. Erstellen Sie in generics/Aquarium.kt eine Funktion isWaterClean(), die ein Aquarium akzeptiert. Sie müssen den generischen Typ des Parameters angeben. Eine Möglichkeit ist die Verwendung von WaterSupply.
fun isWaterClean(aquarium: Aquarium<WaterSupply>) {
   println("aquarium water is clean: ${aquarium.waterSupply.needsProcessing}")
}

Das bedeutet aber, dass Aquarium einen out-Typparameter haben muss, damit es aufgerufen werden kann. Manchmal sind out oder in zu restriktiv, da Sie einen Typ für Ein- und Ausgabe verwenden müssen. Sie können die Anforderung für out entfernen, indem Sie die Funktion generisch machen.

  1. Damit die Funktion generisch ist, setzen Sie nach dem Keyword fun spitze Klammern mit einem generischen Typ T und allen Einschränkungen, in diesem Fall WaterSupply. Ändern Sie Aquarium so, dass es durch T anstelle von WaterSupply eingeschränkt wird.
fun <T: WaterSupply> isWaterClean(aquarium: Aquarium<T>) {
   println("aquarium water is clean: ${!aquarium.waterSupply.needsProcessing}")
}

T ist ein Typparameter für isWaterClean(), mit dem der generische Typ des Aquariums angegeben wird. Dieses Muster ist sehr häufig und es ist eine gute Idee, sich einen Moment Zeit zu nehmen, um es durchzugehen.

  1. Rufen Sie die Funktion isWaterClean() auf, indem Sie den Typ in spitzen Klammern direkt nach dem Funktionsnamen und vor den runden Klammern angeben.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    isWaterClean<TapWater>(aquarium)
}
  1. Aufgrund der Typinferenz aus dem Argument aquarium ist der Typ nicht erforderlich. Entfernen Sie ihn. Führen Sie das Programm aus und beobachten Sie die Ausgabe.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    isWaterClean(aquarium)
}
⇒ aquarium water is clean: false

Schritt 2: Generische Methode mit einem reifizierten Typ erstellen

Sie können auch generische Funktionen für Methoden verwenden, selbst in Klassen mit einem eigenen generischen Typ. In diesem Schritt fügen Sie Aquarium eine generische Methode hinzu, mit der geprüft wird, ob der Typ WaterSupply ist.

  1. Deklarieren Sie in der Klasse Aquarium eine Methode hasWaterSupplyOfType(), die einen generischen Parameter R (T wird bereits verwendet) mit der Einschränkung WaterSupply akzeptiert und true zurückgibt, wenn waterSupply vom Typ R ist. Das ist wie die Funktion, die Sie zuvor deklariert haben, nur innerhalb der Klasse Aquarium.
fun <R: WaterSupply> hasWaterSupplyOfType() = waterSupply is R
  1. Beachten Sie, dass das letzte R rot unterstrichen ist. Bewegen Sie den Mauszeiger darauf, um den Fehler zu sehen.
  2. Für eine is-Prüfung müssen Sie Kotlin mitteilen, dass der Typ reified (real) ist und in der Funktion verwendet werden kann. Dazu setzen Sie inline vor das Keyword fun und reified vor den generischen Typ R.
inline fun <reified R: WaterSupply> hasWaterSupplyOfType() = waterSupply is R

Sobald ein Typ reifiziert wurde, können Sie ihn wie einen normalen Typ verwenden, da er nach dem Inlining ein echter Typ ist. Das bedeutet, dass Sie mit dem Typ is-Prüfungen durchführen können.

Wenn Sie reified hier nicht verwenden, ist der Typ für Kotlin nicht „real“ genug, um is-Prüfungen zuzulassen. Das liegt daran, dass nicht reifizierte Typen nur zur Kompilierzeit verfügbar sind und nicht zur Laufzeit von Ihrem Programm verwendet werden können. Darauf wird im nächsten Abschnitt näher eingegangen.

  1. Übergeben Sie TapWater als Typ. Wie bei generischen Funktionen rufen Sie generische Methoden auf, indem Sie nach dem Funktionsnamen spitze Klammern mit dem Typ verwenden. Führen Sie das Programm aus und sehen Sie sich das Ergebnis an.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    println(aquarium.hasWaterSupplyOfType<TapWater>())   // true
}
⇒ true

Schritt 3: Erweiterungsfunktionen erstellen

Reifizierte Typen können auch für reguläre Funktionen und Erweiterungsfunktionen verwendet werden.

  1. Definieren Sie außerhalb der Klasse Aquarium eine Erweiterungsfunktion für WaterSupply namens isOfType(), die prüft, ob das übergebene WaterSupply einen bestimmten Typ hat, z. B. TapWater.
inline fun <reified T: WaterSupply> WaterSupply.isOfType() = this is T
  1. Rufen Sie die Erweiterungsfunktion wie eine Methode auf.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    println(aquarium.waterSupply.isOfType<TapWater>())  
}
⇒ true

Bei diesen Erweiterungsfunktionen spielt es keine Rolle, welcher Typ von Aquarium verwendet wird (Aquarium, TowerTank oder eine andere Unterklasse), solange es sich um ein Aquarium handelt. Mit der Syntax für die Sternprojektion lassen sich auf einfache Weise verschiedene Übereinstimmungen angeben. Wenn Sie eine Sternprojektion verwenden, sorgt Kotlin dafür, dass Sie nichts Unsicheres tun.

  1. Wenn Sie eine Sternprojektion verwenden möchten, setzen Sie <*> nach Aquarium. Verschiebe hasWaterSupplyOfType() in eine Erweiterungsfunktion, da sie nicht wirklich Teil der Core API von Aquarium ist.
inline fun <reified R: WaterSupply> Aquarium<*>.hasWaterSupplyOfType() = waterSupply is R
  1. Ändern Sie den Aufruf in hasWaterSupplyOfType() und führen Sie das Programm aus.
fun genericsExample() {
    val aquarium = Aquarium(TapWater())
    println(aquarium.hasWaterSupplyOfType<TapWater>())
}
⇒ true

Im vorherigen Beispiel mussten Sie den generischen Typ als reified und die Funktion als inline markieren, da Kotlin sie zur Laufzeit und nicht nur zur Kompilierzeit kennen muss.

Alle generischen Typen werden von Kotlin nur zur Kompilierzeit verwendet. So kann der Compiler sicherstellen, dass Sie alles sicher ausführen. Zur Laufzeit werden alle generischen Typen gelöscht. Daher die frühere Fehlermeldung zum Prüfen eines gelöschten Typs.

Es stellt sich heraus, dass der Compiler korrekten Code erstellen kann, ohne die generischen Typen bis zur Laufzeit beizubehalten. Das bedeutet aber auch, dass Sie manchmal etwas tun, z. B. is-Prüfungen für generische Typen, das der Compiler nicht unterstützen kann. Deshalb wurden in Kotlin reifizierte oder reale Typen eingeführt.

Weitere Informationen zu reified types and type erasure finden Sie in der Kotlin-Dokumentation.

In dieser Lektion ging es um Generics, die wichtig sind, um Code flexibler und leichter wiederverwendbar zu machen.

  • Generische Klassen erstellen, um Code flexibler zu gestalten
  • Fügen Sie generische Einschränkungen hinzu, um die mit Generics verwendeten Typen zu begrenzen.
  • Verwenden Sie die Typen in und out mit Generics, um eine bessere Typprüfung zu ermöglichen und die Typen einzuschränken, die an Klassen übergeben oder von Klassen zurückgegeben werden.
  • Generische Funktionen und Methoden für die Arbeit mit generischen Typen erstellen. Beispiel:
    fun <T: WaterSupply> isWaterClean(aquarium: Aquarium<T>) { ... }
  • Verwenden Sie generische Erweiterungsfunktionen, um einer Klasse nicht zur Kernfunktionalität gehörende Funktionen hinzuzufügen.
  • Reifizierte Typen sind manchmal aufgrund von Typauslöschung erforderlich. Reifizierte Typen bleiben im Gegensatz zu generischen Typen bis zur Laufzeit erhalten.
  • Verwenden Sie die Funktion check(), um zu prüfen, ob Ihr Code wie erwartet ausgeführt wird. Beispiel:
    check(!waterSupply.needsProcessing) { "water supply needs processing first" }

Kotlin-Dokumentation

Wenn Sie weitere Informationen zu einem Thema in diesem Kurs benötigen oder nicht weiterkommen, ist https://kotlinlang.org der beste Ausgangspunkt.

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

Welche der folgenden Optionen ist die Konvention für die Benennung eines generischen Typs?

▢ <Gen>

▢ <Generic>

▢ <T>

▢ <X>

Frage 2

Eine Einschränkung der für einen generischen Typ zulässigen Typen wird als Folgendes bezeichnet:

▢ eine allgemeine Einschränkung

▢ eine generische Einschränkung

▢ Begriffsklärung

▢ ein generisches Typ-Limit

Frage 3

Reified bedeutet:

▢ Die tatsächlichen Auswirkungen der Ausführung eines Objekts wurden berechnet.

▢ Für die Klasse wurde ein eingeschränkter Index für Einträge festgelegt.

▢ Der generische Typparameter wurde in einen echten Typ umgewandelt.

▢ Eine Remote-Fehleranzeige wurde ausgelöst.

Fahren Sie mit der nächsten Lektion fort: 6. Funktionale Manipulation

Eine Übersicht über den Kurs mit Links zu anderen Codelabs finden Sie unter „Kotlin Bootcamp for Programmers: Welcome to the course“.