Versiegelte Hierarchien
Teil des Abschnitts Objektorientierte Programmierung der Kotlin-Journey von Coddy. Lektion 34 von 57.
Eine sealed-Klasse oder -Schnittstelle verfügt über eine abgeschlossene Menge direkter Unterklassen, die alle im selben Paket und Modul deklariert sind. Der Compiler kennt jede von ihnen, was sich für Ergebnisse mit einer festen Anzahl von Formen eignet:
sealed interface Result
data class Success(val value: Int) : Result
data class Failure(val reason: String) : Result
fun parseAge(text: String): Result {
val n = text.toIntOrNull() ?: return Failure("not a number: $text")
return if (n in 0..150) Success(n) else Failure("out of range: $n")
}Innerhalb von main:
println(parseAge("42"))
println(parseAge("abc"))
println(parseAge("200"))Ausgabe:
Success(value=42)
Failure(reason=not a number: abc)
Failure(reason=out of range: 200)Da die Menge abgeschlossen ist, ist ein when über einen versiegelten Typ ohne else vollständig. Wenn später jemand eine Unterklasse hinzufügt, lässt sich jedes solche when nicht mehr kompilieren, bis es den neuen Fall behandelt, sodass kein Fall vergessen wird:
sealed interface Result
data class Success(val value: Int) : Result
data class Failure(val reason: String) : Result
fun describe(r: Result) = when (r) {
is Success -> "ok: ${r.value}"
is Failure -> "error: ${r.reason}"
}Innerhalb von main:
println(describe(Success(7)))
println(describe(Failure("timeout")))Ausgabe:
ok: 7
error: timeoutEin Fall ohne Daten ist ein data object: eine einzelne Instanz mit einer lesbaren toString(). Sein when-Zweig benötigt kein is, da es nur eine Instanz gibt. Die anderen Fälle können Datenklassen, Objekte oder gewöhnliche Klassen sein:
sealed interface Command
data object Quit : Command
data class Move(val steps: Int) : Command
data class Say(val text: String) : Command
fun parse(line: String): Command = when {
line == "quit" -> Quit
line.startsWith("move ") -> Move(line.drop(5).toInt())
else -> Say(line)
}
fun run(c: Command) = when (c) {
Quit -> "bye"
is Move -> "moving ${c.steps}"
is Say -> "saying ${c.text}"
}Innerhalb von main:
for (line in listOf("move 3", "hello", "quit")) {
val c = parse(line)
println("$c: ${run(c)}")
}Ausgabe:
Move(steps=3): moving 3
Say(text=hello): saying hello
Quit: byeEnum oder versiegelte Hierarchie? Ein Enum ist eine feste Menge einzelner Instanzen, die alle dieselben Eigenschaften haben. Eine versiegelte Hierarchie ermöglicht es jedem Fall, eigene Daten zu tragen, und ein Fall wie Move kann viele Instanzen mit unterschiedlichen Werten haben:
enum class Light { RED, YELLOW, GREEN } // gleiche Form, jeweils eine Instanz
sealed interface Payment // jeder Fall hat seine eigenen Daten
data class Card(val number: String) : Payment
data class Transfer(val iban: String, val reference: String) : Payment
data object Cash : PaymentAufgabe
EinfachEin Roboter versteht die Befehle des versiegelten Interfaces Command: Move(steps), Turn(left), Report und Invalid(text), die bereitgestellt werden. Vervollständige parse(line): move 3 ergibt Move(3), turn left und turn right ergeben Turn, report ergibt Report, und alles andere, einschließlich einer Bewegung ohne gültige Zahl, ergibt Invalid mit der Zeile. Vervollständige anschließend Robot.execute(command) mit einem vollständigen when und ohne else. Der Roboter startet bei (0, 0) mit Blickrichtung N (Norden ist y + 1, Osten ist x + 1). Er gibt moved to (0, 3), facing W, at (0, 3) facing W oder ignored zurück.
Der bereitgestellte Code analysiert jede Zeile und gibt Move(steps=3) -> moved to (0, 3) aus.
Dein Code gehört in Robot.kt, Parser.kt, Command.kt, Move.kt, Turn.kt, Report.kt und Invalid.kt. Main.kt enthält den bereitgestellten Eingabe-/Ausgabecode und darf nicht bearbeitet werden.
Probier es selbst
fun main() {
// Gelieferter Ein-/Ausgabecode: belasse ihn wie er ist
val input = generateSequence(::readLine).toList()
val robot = Robot()
for (line in input) {
val command = parse(line)
println("$command -> ${robot.execute(command)}")
}
}
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 – Mitarbeiterverzeichnis7Spezielle Klassen
DatenklassenEnum-KlassenVersiegelte Hierarchienequals, hashCode, toStringRückblick – Bestellverfolgung2Eigenschaften 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