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

Как использовал sealed interface в своих проектах

2.0 Middle🔥 111 комментариев
#Kotlin основы#Архитектура и паттерны

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Использование Sealed Interface и Sealed Class в Android/Kotlin проектах

В своих проектах я активно использую sealed interface (а ранее, до Kotlin 1.5 - sealed class) как мощный инструмент для моделирования ограниченных иерархий типов, особенно при работе с state management, event handling и API response handling. Это один из ключевых паттернов для создания безопасного и выразительного кода.

Основные области применения

1. Моделирование состояний UI (State Management)

Чаще всего sealed интерфейсы используются для представления состояния экрана или компонента.

sealed interface UserProfileState {
    data class Loading(val progress: Int? = null) : UserProfileState
    data class Success(val user: User, val avatarUrl: String?) : UserProfileState
    data class Error(val message: String, val code: Int) : UserProfileState
    object Empty : UserProfileState
}

// Использование в ViewModel или StateFlow
class ProfileViewModel {
    private val _state = MutableStateFlow<UserProfileState>(UserProfileState.Empty)
    val state: StateFlow<UserProfileState> = _state.asStateFlow()
    
    fun loadUser() {
        _state.value = UserProfileState.Loading()
        // ... загрузка данных
    }
}

// Безопасная обработка в UI (Compose или View-based)
when(state) {
    is UserProfileState.Loading -> ShowProgressBar(progress)
    is UserProfileState.Success -> ShowUserData(state.user)
    is UserProfileState.Error -> ShowErrorMessage(state.message)
    UserProfileState.Empty -> ShowPlaceholder()
}

Преимущества: компилятор гарантирует, что мы покрыли все возможные состояния, что исключает ошибки и делает код более надежным.

2. Обработка событий и действий (Event Handling)

Для одноразовых событий (например, навигация, показ снеков) я использую sealed интерфейсы в сочетании с SharedFlow или Channel.

sealed interface ProfileEvent {
    data class ShowToast(val message: String) : ProfileEvent
    data class NavigateToSettings(val userId: String) : ProfileEvent
    data class OpenDialog(val type: DialogType) : ProfileEvent
    object LogoutConfirmed : ProfileEvent
}

class ProfileEventDispatcher {
    private val _events = MutableSharedFlow<ProfileEvent>()
    val events = _events.asSharedFlow()
    
    fun emitEvent(event: ProfileEvent) {
        viewModelScope.launch {
            _events.emit(event)
        }
    }
}

3. Результаты операций и ответы API

При работе с репозиториями и источниками данных sealed интерфейсы идеально подходят для представления результатов.

sealed interface DataResult<out T> {
    data class Success<T>(val data: T) : DataResult<T>
    data class Error(val exception: Throwable, val userMessage: String? = null) : DataResult<Nothing>
    object NetworkUnavailable : DataResult<Nothing>
}

// Использование в репозитории
class UserRepository {
    suspend fun fetchUser(id: String): DataResult<User> {
        return try {
            val response = apiService.getUser(id)
            DataResult.Success(response.toUser())
        } catch (e: IOException) {
            DataResult.NetworkUnavailable
        } catch (e: Exception) {
            DataResult.Error(e, "Failed to load user")
        }
    }
}

4. Конфигурация и параметры компонентов

Для сложных компонентов с различными режимами работы sealed интерфейсы помогают четко определить допустимые конфигурации.

sealed interface CardConfiguration {
    data class Standard(val title: String, val subtitle: String?) : CardConfiguration
    data class Compact(val iconRes: Int, val text: String) : CardConfiguration
    data class Interactive(
        val primaryAction: Action,
        val secondaryActions: List<Action>
    ) : CardConfiguration
    object Placeholder : CardConfiguration
}

Ключевые преимущества и best practices

  • Безопасность типов: Компилятор Kotlin знает все возможные подтипы, что делает when выражения exhaustive без необходимости else ветки.
  • Выразительность: Каждый подтип может содержать специфичные данные (используя data class) или быть маркером (используя object).
  • Совместимость с корутинами и Flow: Sealed интерфейсы идеально сочетаются с современными асинхронными паттернами Kotlin.
  • Эволюция кода: Добавление нового подтипа требует явной обработки во всех when выражениях, что предотвращает забытые случаи.

Переход от Sealed Class к Sealed Interface

После выхода Kotlin 1.5 с поддержкой sealed interface, я начал迁移цию в случаях, где:

  • Необходима возможность реализации несколькими независимыми классами
  • Нужно создавать более сложные иерархии (например, комбинация нескольких sealed интерфейсов)
  • Классы уже имеют другого родителя, но должны быть частью ограниченной иерархии
// Пример комбинации sealed интерфейсов
sealed interface NetworkResult
sealed interface ValidationResult

data class UserData(
    val name: String,
    val email: String
) : NetworkResult, ValidationResult
// Это невозможно с sealed class, но возможно с sealed interface

В итоге, sealed interface стал неотъемлемой частью моей архитектуры Android приложений, значительно повысив читаемость, безопасность и поддерживаемость кода, особенно в сочетании с реактивными паттернами (MVVM, MVI) и современными Android компонентами (Jetpack Compose).