Skip to content
Kotlin

Sealed Classes

Model closed hierarchies and UI state with sealed classes and when.

By EZ4Code Team
sealedwhenadts

Code

// Sealed interface for closed hierarchies
sealed interface Shape {
    fun area(): Double
}

data class Circle(val radius: Double) : Shape {
    override fun area() = Math.PI * radius * radius
}

data class Rectangle(val w: Double, val h: Double) : Shape {
    override fun area() = w * h
}

object Triangle : Shape {
    override fun area() = 0.0
}

fun describe(s: Shape): String = when (s) {
    is Circle -> "circle area=${s.area()}"
    is Rectangle -> "rect area=${s.area()}"
    Triangle -> "triangle placeholder"
    // no else needed - compiler knows all cases
}

fun main() {
    val shapes: List<Shape> = listOf(
        Circle(2.0),
        Rectangle(3.0, 4.0),
        Triangle,
    )
    shapes.forEach(::println)
    shapes.forEach { println(describe(it)) }
}

// Sealed class with nested state for UI events
sealed class UiState<out T> {
    object Loading : UiState<Nothing>()
    data class Success<T>(val data: T) : UiState<T>()
    data class Error(val message: String) : UiState<Nothing>()
}

fun render(state: UiState<String>) = when (state) {
    is UiState.Loading -> "spinner"
    is UiState.Success -> state.data
    is UiState.Error -> "error: ${state.message}"
}

println(render(UiState.Loading))
println(render(UiState.Success("hello")))
println(render(UiState.Error("oops")))

Explanation

Sealed types declare a closed hierarchy, so the compiler can prove exhaustiveness in when expressions without an else branch. They pair naturally with data classes to model algebraic data types like Result or UiState. Adding a new subtype makes the compiler flag every incomplete when, refactoring safely.

More Kotlin Snippets