Полное 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 — локальная установка не требуется.
Все уроки этого курса
- sealed class и sealed interface: когда что использовать
- Полное when с запечатанными иерархиями
- Моделирование состояния интерфейса с помощью запечатанных классов
- Вложенные и объединённые запечатанные иерархии