Sealed class в Kotlin: состояния экрана и исчерпывающий when

Sealed class в Kotlin: состояния экрана и исчерпывающий when
Содержание статьи

Sealed class позволяет описать известный набор вариантов одного состояния. Вместо независимых флагов loading и error можно передавать один объект: загрузка, данные или ошибка. В каждом варианте хранятся только относящиеся к нему значения, а when помогает проверить полноту обработки.

Три состояния с разными данными

Для небольшого примера не нужен Android-проект. Обычная функция Kotlin возвращает текстовое представление состояния списка задач.

sealed class TasksState {
    object Loading : TasksState()
    data class Data(val titles: List<String>) : TasksState()
    data class Error(val message: String) : TasksState()
}

fun describe(state: TasksState): String = when (state) {
    TasksState.Loading -> "Загрузка"
    is TasksState.Data -> "Задач: ${state.titles.size}"
    is TasksState.Error -> "Ошибка: ${state.message}"
}

У Loading нет дополнительных данных. В Data есть список названий, в Error сообщение. После проверки через is компилятор уточняет тип внутри соответствующей ветки, поэтому свойства titles и message доступны без ручного приведения.

Зачем обходиться без else

Когда when используется как выражение и перечисляет все варианты этой sealed-иерархии, отдельная ветка else не нужна. Если позже добавить новый вариант, например Empty, компилятор потребует учесть его там, где исчерпывающая обработка больше не обеспечена.

Широкая ветка else может скрыть такое изменение за универсальным текстом. Это бывает уместно по контракту, но для явного отображения состояний полезнее проверить каждый вариант. Пустой список в Data сам по себе допустим: решите, нужен ли ему отдельный вариант Empty или достаточно проверки размера внутри Data.

Где находятся границы иерархии

Прямые наследники sealed-класса или интерфейса должны находиться в том же пакете и модуле. Они имеют определённое имя и не могут быть локальными либо анонимными. В примере наследники вложены в один класс, что делает набор вариантов видимым рядом.

Для многоплатформенных проектов действуют дополнительные правила исходных наборов. Также открытый прямой наследник может допускать дальнейшее расширение. Поэтому sealed не означает, что любое косвенное наследование во всей программе запрещено. Если общая реализация класса не нужна, похожую модель можно строить через sealed interface.

Что проверить в модели состояния

  • Функция обрабатывает Loading, Data и Error отдельно.
  • Пустые данные имеют заранее определённое отображение.
  • Добавление нового варианта требует пересмотра соответствующих when-выражений.
  • Переходы между состояниями отражают сценарии продукта, включая повторную загрузку.

Тип не проверяет текст ошибки и не подтверждает корректность данных с сервера. Поле List<String> предоставляет интерфейс чтения, но само по себе не гарантирует глубокую неизменяемость всех исходных объектов. Если экран должен одновременно показывать старые данные и индикатор обновления, модель из трёх взаимоисключающих вариантов нужно расширить осмысленно, а не добавлять случайные флаги.

Изучить моделирование данных можно на курсах Kotlin. На Mentoring.digital можно подобрать наставника по Kotlin и разобрать состояния конкретного экрана вместе с переходами и ошибками.

Похожие статьи по данной теме