Kotlin sealed interface — Type-Safe State, Events & Navigation
Sealed classes and sealed interfaces are powerful Kotlin features that ensure type-safe hierarchies. Let's explore how they solve real Android patterns.
sealed class vs sealed interface
sealed class restricts inheritance in the same package. sealed interface (Kotlin 1.6+) adds multi-inheritance:
sealed interface UiState
sealed class DataState : UiState
data class Loading : DataState()
data class Success(val data: String) : DataState()
data class Error(val exception: Exception) : DataState()
// Multiple inheritance
class CachedState(val cached: String) : DataState(), Serializable
UI State Pattern
Use sealed classes for exhaustive when expressions:
sealed class ScreenState {
object Loading : ScreenState()
data class Content(val items: List<String>) : ScreenState()
data class Error(val message: String) : ScreenState()
object Empty : ScreenState()
}
fun render(state: ScreenState) {
when (state) {
is ScreenState.Loading -> showSpinner()
is ScreenState.Content -> showList(state.items)
is ScreenState.Error -> showError(state.message)
is ScreenState.Empty -> showEmpty()
// Compiler ensures all cases covered
}
}
User Action sealed interface Pattern
sealed interface UserAction {
data class Search(val query: String) : UserAction
data class Select(val id: Int) : UserAction
object Refresh : UserAction
data class Delete(val id: Int) : UserAction
}
class ViewModel : ViewModel() {
fun onAction(action: UserAction) {
when (action) {
is UserAction.Search -> search(action.query)
is UserAction.Select -> select(action.id)
UserAction.Refresh -> refresh()
is UserAction.Delete -> delete(action.id)
}
}
}
Navigation Routes with property
sealed class Route(val path: String) {
object Home : Route("home")
data class Details(val id: Int) : Route("details/$id")
object Settings : Route("settings")
}
fun navigate(route: Route) {
navController.navigate(route.path)
}
Error handling sealed hierarchy
sealed class AppError(val message: String) {
class NetworkError(msg: String = "No internet") : AppError(msg)
class ServerError(val code: Int, msg: String) : AppError(msg)
class AuthError(msg: String = "Unauthorized") : AppError(msg)
class UnknownError(val throwable: Throwable) : AppError(throwable.message ?: "Unknown")
}
fun Throwable.toAppError(): AppError = when (this) {
is IOException -> AppError.NetworkError()
is HttpException -> AppError.ServerError(code, message ?: "")
is SecurityException -> AppError.AuthError()
else -> AppError.UnknownError(this)
}
Sealed types prevent runtime surprises and enable compile-time safety for Android patterns.
Top comments (0)