Что такое sealed class и когда её применять?
В Kotlin sealed class — это специальный тип абстрактного класса, который ограничивает множество его прямых наследников. Такие классы объявляются с помощью ключевого слова sealed и позволяют строго контролировать иерархию типов, что делает их особенно полезными для выражения ограниченных наборов состояний, например, в конечных автоматах, DSL, обработке ошибок, UI-состояниях и др.
Основная идея sealed class
Ключевая особенность sealed class — все её подклассы должны быть определены в одном и том же файле. Это позволяет компилятору знать полный перечень возможных подтипов, что делает такие классы идеальными для использования с when-выражениями — особенно без необходимости писать else.
Синтаксис
sealed class Result
data class Success(val data: String) : Result()
data class Error(val exception: Throwable) : Result()
object Loading : Result()
Здесь Result — это sealed class, и у неё три возможных наследника: Success, Error и Loading.
Использование в when
Поскольку компилятору известны все подклассы sealed class, when может быть использовано без else:
fun handle(result: Result) {
when (result) {
is Success -> println("Data: ${result.data}")
is Error -> println("Error: ${result.exception}")
is Loading -> println("Loading...")
// else не нужен — все случаи покрыты
}
}
Отличие от enum class
И sealed, и enum позволяют описать ограниченное множество состояний, но:
Aspect | sealed class | enum class |
---|---|---|
Наследование | Поддерживает вложенные типы с состоянием | Фиксированные однотипные значения |
--- | --- | --- |
Поля | Могут иметь разные свойства и данные | Один набор полей на все элементы |
--- | --- | --- |
Расширяемость | Каждый наследник может быть полноценным классом | Жёстко фиксированная структура |
--- | --- | --- |
Использование | Подходит для сложных и гетерогенных состояний | Простые ограниченные перечисления |
--- | --- | --- |
Применение sealed class
1. Обработка результата операций
sealed class NetworkResult
data class NetworkSuccess(val body: String) : NetworkResult()
data class NetworkFailure(val code: Int, val error: String) : NetworkResult()
object NetworkLoading : NetworkResult()
Позволяет элегантно обрабатывать различные состояния сетевого запроса.
2. Реализация состояния экрана (UI state)
sealed class UiState
object Idle : UiState()
object Loading : UiState()
data class Success(val items: List<String>) : UiState()
data class Error(val message: String) : UiState()
Такой подход облегчает поддержку однозначной архитектуры и управления отображением.
3. Алгебраические типы данных / выражения DSL
sealed class Expr
data class Const(val value: Int) : Expr()
data class Sum(val left: Expr, val right: Expr) : Expr()
data class Mul(val left: Expr, val right: Expr) : Expr()
Функция для вычисления выражения:
fun eval(expr: Expr): Int = when (expr) {
is Const -> expr.value
is Sum -> eval(expr.left) + eval(expr.right)
is Mul -> eval(expr.left) \* eval(expr.right)
}
Поддержка модификаторов
-
sealed class — всегда абстрактный, даже без ключевого слова abstract
-
Подклассы могут быть:
-
data class
-
object
-
class (своим поведением)
-
-
Саму sealed class нельзя создать напрямую
Наследование и размещение
-
Все непосредственные наследники должны быть в одном и том же файле.
-
Они могут находиться внутри sealed class (вложенными) или вне её (в том же файле).
-
Можно делать sealed interface (начиная с Kotlin 1.5+), аналогично по смыслу.
Отличие от abstract class
Критерий | sealed class | abstract class |
---|---|---|
Ограничение на подклассы | Да, только в одном файле | Нет ограничений |
--- | --- | --- |
Использование в when без else | Да | Нет |
--- | --- | --- |
Применение | Моделирование ограниченного набора вариантов | Базовая абстракция без ограничений |
--- | --- | --- |
Пример вложенных sealed классов
sealed class Shape {
data class Circle(val radius: Float) : Shape()
data class Rectangle(val width: Float, val height: Float) : Shape()
}
Возможность sealed interface
С Kotlin 1.5 появилась возможность создавать sealed interface:
sealed interface Event
data class Click(val x: Int, val y: Int) : Event
object Close : Event
Это удобно, когда нужен полиморфизм без конкретной реализации (наследования от класса).
sealed class особенно ценна в функциональном стиле программирования, где важно чётко выражать конечные множества возможных случаев и обрабатывать их исчерпывающим образом. Она позволяет моделировать бизнес-логику, не полагаясь на null, исключения или непредсказуемое поведение, улучшая безопасность типов и читаемость кода.