Hierarchie zapieczętowane
Część sekcji Programowanie obiektowe ścieżki Kotlin w Coddy. Lekcja 34 z 57.
Klasa lub interfejs sealed ma zamknięty zbiór bezpośrednich podklas, z których wszystkie są zadeklarowane w tym samym pakiecie i module. Kompilator zna każdą z nich, co sprawdza się w przypadku wyników o ustalonej liczbie postaci:
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")
}Wewnątrz main:
println(parseAge("42"))
println(parseAge("abc"))
println(parseAge("200"))Wynik:
Success(value=42)
Failure(reason=not a number: abc)
Failure(reason=out of range: 200)Ponieważ zbiór jest zamknięty, wyrażenie when dotyczące typu zapieczętowanego jest wyczerpujące bez else. Jeśli ktoś później doda podklasę, każde takie when przestanie się kompilować, dopóki nie obsłuży nowego przypadku, więc żaden przypadek nie zostanie pominięty:
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}"
}Wewnątrz main:
println(describe(Success(7)))
println(describe(Failure("timeout")))Wynik:
ok: 7
error: timeoutPrzypadek bez danych to data object: pojedyncza instancja z czytelną metodą toString(). Jego gałąź when nie wymaga is, ponieważ istnieje tylko jedna instancja. Pozostałe przypadki mogą być klasami danych, obiektami lub zwykłymi klasami:
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}"
}Wewnątrz main:
for (line in listOf("move 3", "hello", "quit")) {
val c = parse(line)
println("$c: ${run(c)}")
}Wynik:
Move(steps=3): moving 3
Say(text=hello): saying hello
Quit: byeWyliczenie czy zapieczętowana hierarchia? Wyliczenie to stały zbiór pojedynczych instancji, które mają te same właściwości. Zapieczętowana hierarchia pozwala każdemu przypadkowi przechowywać własne dane, a przypadek taki jak Move może mieć wiele instancji z różnymi wartościami:
enum class Light { RED, YELLOW, GREEN } // taka sama struktura, po jednej instancji każdego
sealed interface Payment // każdy wariant ma własne dane
data class Card(val number: String) : Payment
data class Transfer(val iban: String, val reference: String) : Payment
data object Cash : PaymentWyzwanie
ŁatwyRobot rozumie polecenia zapieczętowanego interfejsu Command: Move(steps), Turn(left), Report i Invalid(text), które są dostarczone. Uzupełnij parse(line): move 3 daje Move(3), turn left i turn right dają Turn, report daje Report, a wszystko inne, w tym ruch bez prawidłowej liczby, daje Invalid z linią. Następnie uzupełnij Robot.execute(command), używając wyczerpującego wyrażenia when bez else. Robot zaczyna w punkcie (0, 0), zwrócony w kierunku N (północ to y + 1, wschód to x + 1). Zwraca moved to (0, 3), facing W, at (0, 3) facing W lub ignored.
Dostarczony kod parsuje każdą linię i wypisuje Move(steps=3) -> moved to (0, 3).
Twój kod umieść w plikach Robot.kt, Parser.kt, Command.kt, Move.kt, Turn.kt, Report.kt i Invalid.kt. Main.kt zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.
Spróbuj swoich sił
fun main() {
// Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
val input = generateSequence(::readLine).toList()
val robot = Robot()
for (line in input) {
val command = parse(line)
println("$command -> ${robot.execute(command)}")
}
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Klasy i obiekty
Praca z plikamiDefiniowanie klasKonstruktory główneBloki init i walidacjaKonstruktory dodatkowePodsumowanie — karta biblioteczna4Dziedziczenie
Klasy otwarte i podklasyNadpisywanie składowychsuper i konstruktoryKlasy abstrakcyjnePodsumowanie – katalog pracowników7Klasy specjalne
Klasy danychKlasy wyliczenioweHierarchie zapieczętowaneequals, hashCode, toStringPodsumowanie — śledzenie zamówień10Wzorce projektowe, część 2
Delegowanie za pomocą byWzorzec dekoratoraWzorzec budowniczegoWzorzec stanu2Właściwości i hermetyzacja
Własne akcesoryModyfikatory widocznościPrywatne setterylazy i lateinitPowtórka – konto bankowe5Interfejsy
Deklarowanie interfejsówMetody domyślneWiele interfejsówInterfejsy jako typyPowtórzenie – metody płatności8Typy generyczne
Funkcje generyczneKlasy generyczneOgraniczenia typówKowariancja i kontrawariancja: in i outPodsumowanie — kolejka priorytetowa11Projekt: Zarządzanie biblioteką
Książki i czytelnicyWypożyczanie książekPoćwicz samodzielnie: Edytor online Kotlin