← Назад к вопросам
В чем разница между Sealed Interface и Sealed Class?
2.0 Middle🔥 111 комментариев
#Kotlin основы#Архитектура и паттерны
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Sealed Interface vs Sealed Class в Kotlin
Определение
Sealed класс/интерфейс — это способ ограничить иерархию наследования. Только классы, определённые в одном файле (или том же пакете в Kotlin 1.0), могут наследоваться от sealed типа.
Sealed Class
sealed class Result {
data class Success(val data: String) : Result()
data class Error(val exception: Exception) : Result()
object Loading : Result()
}
fun handleResult(result: Result) {
when (result) {
is Result.Success -> println(result.data)
is Result.Error -> println(result.exception)
is Result.Loading -> println("Loading...")
// ✅ Compiler требует охватить все кейсы
}
}
Характеристики:
- Может иметь конструктор
- Может иметь состояние (поля)
- Может иметь методы с реализацией
- Подклассы могут быть data, object, regular классами
- Абстрактные методы обязательны
Sealed Interface (Kotlin 1.7+)
sealed interface Result {
data class Success(val data: String) : Result
data class Error(val exception: Exception) : Result
object Loading : Result
}
fun handleResult(result: Result) {
when (result) {
is Result.Success -> println(result.data)
is Result.Error -> println(result.exception)
is Result.Loading -> println("Loading...")
}
}
Характеристики:
- БЕЗ конструктора
- БЕЗ состояния
- Только абстрактные методы
- Реализации могут быть классами, object-ами, интерфейсами
- Множественное наследование
Сравнительная таблица
| Аспект | Sealed Class | Sealed Interface |
|---|---|---|
| Конструктор | ✅ Есть | ❌ Нет |
| Состояние | ✅ Можно | ❌ Нет (только val) |
| Методы | ✅ С реализацией | ⚠️ Только абстрактные |
| Наследование | ❌ Одно | ✅ Множественное |
| Расширение в других пакетах (Kotlin 1.1+) | ❌ Только в одном файле | ⚠️ В одном пакете |
| Использование в when | ✅ Полно | ✅ Полно |
Практические примеры
Sealed Class — когда нужно состояние:
sealed class User {
abstract val id: Int
abstract val name: String
data class AuthenticatedUser(
override val id: Int,
override val name: String,
val token: String
) : User()
data class GuestUser(
override val id: Int = -1,
override val name: String = "Guest"
) : User()
}
val user: User = User.AuthenticatedUser(1, "John", "token123")
when (user) {
is User.AuthenticatedUser -> println("Авторизован: ${user.name}, токен: ${user.token}")
is User.GuestUser -> println("Гость")
}
Sealed Interface — для контрактов:
sealed interface NetworkResult<out T> {
data class Success<T>(val data: T) : NetworkResult<T>
data class Error(val code: Int, val message: String) : NetworkResult<Nothing>
class Loading<T> : NetworkResult<T>
}
fun <T> handleNetworkResult(result: NetworkResult<T>) {
when (result) {
is NetworkResult.Success -> println("Успех: ${result.data}")
is NetworkResult.Error -> println("Ошибка ${result.code}: ${result.message}")
is NetworkResult.Loading -> println("Загрузка...")
}
}
Сложная иерархия с интерфейсом
sealed interface Event {
data class Click(val x: Int, val y: Int) : Event
data class KeyPress(val key: Int) : Event
}
sealed interface UiEvent : Event {
data class ButtonClicked(val buttonId: String) : UiEvent
data class InputChanged(val text: String) : UiEvent
}
fun handleEvent(event: Event) {
when (event) {
is Click -> println("Клик в ($event.x, $event.y)")
is KeyPress -> println("Нажата клавиша $event.key")
is UiEvent.ButtonClicked -> println("Кнопка $event.buttonId")
is UiEvent.InputChanged -> println("Введено: $event.text")
}
}
Ковариантность в Sealed Interface
// ❌ Sealed Class не поддерживает out
sealed class Result<out T> { // Ошибка!
data class Success<T>(val data: T) : Result<T>()
}
// ✅ Sealed Interface поддерживает
sealed interface Result<out T> {
data class Success<T>(val data: T) : Result<T>
data class Error(val message: String) : Result<Nothing>
}
// Благодаря out, можно сделать:
val successInt: Result<Int> = Result.Success(42)
val resultAny: Result<Any> = successInt // ✅ Работает!
Когда использовать что
Sealed Class:
- Нужно состояние или общие поля
- Нужны методы с реализацией
- Нужен конструктор с параметрами
- Пример:
Result<T>с методомgetOrNull()
Sealed Interface:
- Только определение контракта
- Нужна максимальная гибкость
- Нужна ковариантность
- Нужно множественное наследование
- Пример:
Result<T>,Event,UiState
Итог
Sealed Interface — более гибкий и современный подход (Kotlin 1.7+), предпочтителен для контрактов и ADT. Sealed Class остаётся оптимальным, когда нужно состояние и методы с реализацией.