0Pricing
Kotlin Academy · Урок

Полное when с запечатанными иерархиями

Пишите полные выражения when, охватывающие каждый вариант запечатанной иерархии.

«Полное when с запечатанными иерархиями» — бесплатный урок Kotlin Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Kotlin Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Kotlin Academy содержит 4 уроков всего.

Исчерпывающий when

Выражение when, используемое как выражение, должно быть исчерпывающим — необходимо обработать каждое возможное значение. Запечатанные иерархии позволяют компилятору проверить это за Вас.

Неисчерпывающий when для открытого типа

Для обычных классов компилятор не может проверить все подтипы — требуется ветвь else.

open class Shape
class Circle : Shape()
class Square : Shape()
fun describe(s: Shape) = when (s) {
    is Circle -> "circle"
    is Square -> "square"
    else      -> "unknown" // required
}
fun main() { println(describe(Circle())) }

Исчерпывающий when для sealed

Для запечатанного класса компилятор знает все подтипы. Если охвачены все варианты, else не нужен.

sealed class Result
class Success(val value: Int) : Result()
class Failure(val msg: String) : Result()
fun describe(r: Result) = when (r) {
    is Success -> "ok: ${r.value}"
    is Failure -> "err: ${r.msg}"
}
fun main() {
    println(describe(Success(42)))
    println(describe(Failure("nope")))
}

Sealed interface

Запечатанные интерфейсы обеспечивают проверку полноты даже тогда, когда классам нужно множественное наследование.

sealed interface Animal
class Dog : Animal
class Cat : Animal
fun speak(a: Animal) = when (a) {
    is Dog -> "woof"
    is Cat -> "meow"
}
fun main() {
    println(speak(Dog()))
    println(speak(Cat()))
}

Умные приведения типов в ветвях

Внутри каждой ветви is Kotlin автоматически приводит переменную к конкретному типу — ручное приведение не требуется.

sealed class Event
data class Click(val x: Int, val y: Int) : Event()
data class Key(val char: Char) : Event()
fun handle(e: Event) = when (e) {
    is Click -> "clicked at (${e.x},${e.y})" // smart-cast
    is Key   -> "key=${e.char}"
}
fun main() {
    println(handle(Click(10, 20)))
    println(handle(Key('A')))
}

Забытый вариант

Если позже добавить новый вариант, каждое исчерпывающее when, в котором запечатанный тип по-прежнему используется как выражение, перестанет компилироваться — это отличный механизм защиты.

sealed class Status
object Idle : Status()
object Running : Status()
// If we add object Done : Status(), every exhaustive when must update.
fun text(s: Status): String = when (s) {
    is Idle -> "idle"
    is Running -> "running"
}
fun main() { println(text(Idle)) }

Sealed с классами данных

Распространённый шаблон: классы данных как варианты запечатанной иерархии.

sealed class UiState {
    object Loading : UiState()
    data class Success(val data: List<String>) : UiState()
    data class Error(val message: String) : UiState()
}
fun render(state: UiState) = when (state) {
    UiState.Loading -> "loading..."
    is UiState.Success -> "got ${state.data.size} items"
    is UiState.Error -> "error: ${state.message}"
}
fun main() {
    println(render(UiState.Loading))
    println(render(UiState.Success(listOf("a", "b"))))
    println(render(UiState.Error("404")))
}

Использование when как инструкции

При использовании в качестве инструкции, а не выражения, when не обязан быть исчерпывающим. Но Вы отказываетесь от этого механизма защиты.

sealed class Cmd
object Start : Cmd()
object Stop : Cmd()
fun execute(c: Cmd) {
    // Statement form: no error if incomplete (avoid this style)
    when (c) {
        Start -> println("started")
        Stop -> println("stopped")
    }
}
fun main() { execute(Start); execute(Stop) }

Принудительная проверка полноты инструкции

Присвойте результат when значению (или используйте приём с .exhaustive), чтобы компилятор проверил наличие всех вариантов.

sealed class Cmd
object Start : Cmd()
object Stop : Cmd()
fun execute(c: Cmd) {
    val handled: Unit = when (c) { // expression context now
        Start -> println("started")
        Stop -> println("stopped")
    }
}
fun main() { execute(Start) }

Ветви с несколькими вариантами

Объединяйте варианты с помощью запятых, чтобы использовать одно тело для нескольких вариантов.

sealed class Event
class Click : Event()
class Tap : Event()
class Drag : Event()
fun isPointer(e: Event) = when (e) {
    is Click, is Tap, is Drag -> true
}
fun main() { println(isPointer(Tap())) }

Вложенные запечатанные типы

Запечатанные иерархии могут быть вложенными — проверка полноты по-прежнему работает для глубоких деревьев.

sealed class Network {
    sealed class Wifi : Network() {
        object Open : Wifi()
        data class Secured(val ssid: String) : Wifi()
    }
    object Cellular : Network()
}
fun describe(n: Network) = when (n) {
    Network.Wifi.Open -> "open wifi"
    is Network.Wifi.Secured -> "secured: ${n.ssid}"
    Network.Cellular -> "cellular"
}
fun main() {
    println(describe(Network.Wifi.Secured("home")))
    println(describe(Network.Cellular))
}

Быстрая проверка

Какое главное преимущество даёт использование when с запечатанными иерархиями?

Итоги

Используйте запечатанные классы и интерфейсы вместе с when для проверки полноты во время компиляции. Умные приведения упрощают тела ветвей. Используйте конструкцию как выражение (или присвойте её результат), чтобы принудительно выполнить проверку; объединяйте варианты запятыми; вкладывайте запечатанные типы для представления состояния в виде дерева.

Часто задаваемые вопросы

Урок «Полное when с запечатанными иерархиями» бесплатный?

Да — полный текст урока «Полное when с запечатанными иерархиями» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Kotlin Academy, подпишись на CoddyKit PRO. Курс Kotlin Academy содержит 4 уроков всего.

Чему я научусь в уроке «Полное when с запечатанными иерархиями»?

Пишите полные выражения when, охватывающие каждый вариант запечатанной иерархии. Ты практикуешь Kotlin Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Kotlin Academy?

Предыдущий опыт не требуется. Kotlin Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «Полное when с запечатанными иерархиями»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Kotlin Academy?

Да. Каждый урок Kotlin Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. sealed class и sealed interface: когда что использовать
  2. Полное when с запечатанными иерархиями
  3. Моделирование состояния интерфейса с помощью запечатанных классов
  4. Вложенные и объединённые запечатанные иерархии
← Назад к Kotlin Academy