← Назад к вопросам

В чем разница между 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 ClassSealed 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 остаётся оптимальным, когда нужно состояние и методы с реализацией.