sealed class и иерархии
Учимся описывать строго ограниченный набор вариантов одного типа.
sealed class — класс, все наследники которого известны на этапе компиляции и объявлены в том же файле (модуле).
Иногда нужно описать тип, у которого ровно несколько вариантов: результат операции — это либо успех, либо ошибка; состояние загрузки — либо «грузим», либо «готово», либо «сбой». Для таких закрытых иерархий идеален sealed class.
Закрытая иерархия
sealed запрещает создавать наследников где-либо, кроме того же файла. Компилятор знает полный список подтипов.
sealed class Result
class Success(val data: String) : Result()
class Error(val message: String) : Result()
fun handle(r: Result): String = when (r) {
is Success -> "Данные: ${r.data}"
is Error -> "Ошибка: ${r.message}"
}
fun main() {
println(handle(Success("OK")))
println(handle(Error("нет связи")))
}Вывод:
Данные: OK Ошибка: нет связи
Исчерпывающий when без else
Раз компилятор знает все подтипы sealed-класса, в when можно не писать else — достаточно покрыть все варианты. Более того, если вы добавите новый подтип и забудете обработать его, компилятор укажет на это.
sealed class Shape
class Circle(val r: Double) : Shape()
class Square(val side: Double) : Shape()
fun area(s: Shape): Double = when (s) {
is Circle -> 3.14 * s.r * s.r
is Square -> s.side * s.side
// else не нужен — все случаи покрыты
}
fun main() {
println(area(Circle(2.0)))
println(area(Square(3.0)))
}Вывод:
12.56 9.0
Где это полезно
sealed class идеально моделирует состояния и результаты: загрузка экрана (Loading/Content/Failure), исход операции (Ok/Fail), узлы дерева. Это безопаснее набора булевых флагов: невозможные комбинации просто нельзя выразить.
Как работает под капотом
В ветках when (r) { is Success -> ... } снова работает smart cast: после проверки is Success компилятор уже знает точный тип и даёт обращаться к r.data без приведения. А «исчерпывающесть» (exhaustiveness) — это статическая проверка компилятора: он сверяет список веток с полным списком наследников и требует обработать каждый, если when используется как выражение.
Частые ошибки
- Объявлять наследников
sealed-класса в другом файле/модуле. Это запрещено — теряется гарантия полноты. - Добавлять
else«на всякий случай». Тогда компилятор перестанет напоминать о новом необработанном подтипе. - Путать
sealedсenum.enum— фиксированный набор значений,sealed— набор подтипов, каждый со своими данными.
Итог
sealed classзадаёт закрытый, известный компилятору набор подтипов.- В
whenпо такому типуelseне нужен — покрываются все случаи. - Компилятор подсказывает, если новый подтип не обработан.
- Отлично подходит для моделирования состояний и результатов.