Generische Klassen
Teil des Abschnitts Objektorientierte Programmierung der Kotlin-Journey von Coddy. Lektion 38 von 57.
Eine Klasse kann ebenfalls Typparameter haben. Box<T> enthält einen Wert eines beliebigen Typs, und jede Instanz legt den Typ fest: Eine Box<Int> akzeptiert nur Int-Werte:
class Box<T>(var value: T) {
fun describe() = "box with $value"
}Innerhalb von main:
val numberBox = Box(42)
val textBox = Box("hello")
numberBox.value += 1
println(numberBox.describe())
println(textBox.describe())
// numberBox.value = "x" // Fehler: ein String ist kein IntAusgabe:
box with 43
box with helloContainer sind die typischen generischen Klassen. Eine Stack<T>-Implementierung funktioniert für jeden Elementtyp, und der Compiler überprüft, was hineingelangt und herauskommt:
class Stack<T> {
private val items = mutableListOf<T>()
val size: Int
get() = items.size
fun push(item: T) { items.add(item) }
fun pop(): T? = items.removeLastOrNull()
fun peek(): T? = items.lastOrNull()
}Innerhalb von main:
val stack = Stack<String>()
stack.push("a")
stack.push("b")
println(stack.pop())
println(stack.peek())
println(stack.size)Ausgabe:
b
a
1Das Typargument wird aus den Konstruktorargumenten abgeleitet, sofern welche vorhanden sind, wie bei Box(42). Andernfalls gib es an: Stack<String>(). Instanzen mit unterschiedlichen Typargumenten sind unterschiedliche Typen, daher wird ein Stack<String> niemals mit einem Stack<Int> verwechselt. Funktionen einer generischen Klasse können auch eigene Typparameter hinzufügen:
class Box<T>(val value: T) {
fun <R> map(f: (T) -> R): Box<R> = Box(f(value))
}Innerhalb von main:
val length = Box("kotlin").map { it.length }
println(length.value + 1)Ausgabe:
7Generische Schnittstellen folgen denselben Regeln. Eine Klasse kann eine solche Schnittstelle generisch oder für einen bestimmten Typargumenttyp implementieren:
interface Repository<T> {
fun add(item: T)
fun all(): List<T>
}
class MemoryRepository<T> : Repository<T> {
private val items = mutableListOf<T>()
override fun add(item: T) { items.add(item) }
override fun all() = items.toList()
}
class TagRepository : Repository<String> {
private val tags = sortedSetOf<String>()
override fun add(item: String) { tags.add(item.lowercase()) }
override fun all() = tags.toList()
}Innerhalb von main:
val ids = MemoryRepository<Int>()
ids.add(3); ids.add(1)
val tags = TagRepository()
tags.add("Kotlin"); tags.add("JVM")
println("${ids.all()} ${tags.all()}")Ausgabe:
[3, 1] [jvm, kotlin]Aufgabe
EinfachSchreibe die generische Klasse History<T>(limit), die sich die letzten limit Elemente merkt. add(item) fügt ein Element hinzu und entfernt das älteste, wenn mehr als limit Elemente vorhanden sind. latest ist das neueste Element oder null, und all() gibt die Elemente vom ältesten bis zum neuesten als schreibgeschützte Liste zurück. Schreibe dann die Erweiterung History<Int>.average(), die nur für Verläufe von Zahlen existiert und den ganzzahligen Durchschnitt der gespeicherten Zahlen zurückgibt (bei leerem Verlauf 0).
Der bereitgestellte Code liest das Limit, eine Zeile mit Wörtern und eine Zeile mit Zahlen ein. Er füllt ein History<String> und ein History<Int> und gibt die gespeicherten Wörter, das neueste Wort, die gespeicherten Zahlen, ihren Durchschnitt und empty latest: null aus.
Dein Code gehört in History.kt. Main.kt enthält den bereitgestellten Eingabe-/Ausgabecode und darf nicht bearbeitet werden.
Probier es selbst
fun main() {
// Bereitgestellter Ein-/Ausgabe-Code: belasse ihn wie er ist
val input = generateSequence(::readLine).toList()
val limit = input[0].toInt()
val words = History<String>(limit)
for (w in input[1].split(" ")) words.add(w)
val numbers = History<Int>(limit)
for (n in input[2].split(" ")) numbers.add(n.toInt())
println("words: ${words.all()}")
println("latest word: ${words.latest}")
println("numbers: ${numbers.all()}")
println("average: ${numbers.average()}")
println("empty latest: ${History<Char>(3).latest}")
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Klassen und Objekte
Arbeiten mit DateienKlassen definierenPrimärkonstruktoreninit-Blöcke und ValidierungSekundärkonstruktorenRückblick – Bibliotheksausweis4Vererbung
Offene Klassen und UnterklassenÜberschreiben von Membernsuper und KonstruktorenAbstrakte KlassenRückblick – Mitarbeiterverzeichnis2Eigenschaften und Kapselung
Benutzerdefinierte ZugriffsmethodenSichtbarkeitsmodifikatorenPrivate Setterlazy und lateinitWiederholung – Bankkonto5Schnittstellen
Schnittstellen deklarierenStandardmethodenMehrere SchnittstellenSchnittstellen als TypenRückblick – Zahlungsmethoden8Generics
Generische FunktionenGenerische KlassenTypbeschränkungenVarianz mit in und outRückblick – Prioritätswarteschlange11Projekt: Bibliotheksverwaltung
Bücher und MitgliederBücher ausleihenÜbe selbstständig: Kotlin-Playground