Menu
Coddy logo textTech

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: timeout

Przypadek 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: bye

Wyliczenie 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 : Payment
challenge icon

Wyzwanie

Łatwy

Robot 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)}")
    }
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Edytor online Kotlin