kotlinintermediate
Sealed Classes and Exhaustive when
Model restricted hierarchies with sealed classes: exhaustive when, data objects, and state machines.
kotlinPress ⌘/Ctrl + Shift + C to copy
// API Result type
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val message: String, val code: Int = 500) : Result<Nothing>()
data object Loading : Result<Nothing>()
}
// State machine
sealed interface OrderState {
data object Pending : OrderState
data class Processing(val startedAt: Long) : OrderState
data class Shipped(val trackingId: String) : OrderState
data class Delivered(val deliveredAt: Long) : OrderState
data class Cancelled(val reason: String) : OrderState
}
// Expression tree
sealed interface Expr {
data class Num(val value: Double) : Expr
data class Add(val left: Expr, val right: Expr) : Expr
data class Mul(val left: Expr, val right: Expr) : Expr
data class Neg(val expr: Expr) : Expr
}
fun eval(expr: Expr): Double = when (expr) {
is Expr.Num -> expr.value
is Expr.Add -> eval(expr.left) + eval(expr.right)
is Expr.Mul -> eval(expr.left) * eval(expr.right)
is Expr.Neg -> -eval(expr.expr)
// No else needed — exhaustive!
}
fun prettyPrint(expr: Expr): String = when (expr) {
is Expr.Num -> expr.value.toString()
is Expr.Add -> "(${prettyPrint(expr.left)} + ${prettyPrint(expr.right)})"
is Expr.Mul -> "(${prettyPrint(expr.left)} * ${prettyPrint(expr.right)})"
is Expr.Neg -> "(-${prettyPrint(expr.expr)})"
}
// Process result
fun <T> handleResult(result: Result<T>): String = when (result) {
is Result.Success -> "Data: ${result.data}"
is Result.Error -> "Error ${result.code}: ${result.message}"
Result.Loading -> "Loading..."
}
// State transitions
fun transition(state: OrderState, action: String): OrderState = when (state) {
is OrderState.Pending -> when (action) {
"process" -> OrderState.Processing(System.currentTimeMillis())
"cancel" -> OrderState.Cancelled("Customer request")
else -> state
}
is OrderState.Processing -> when (action) {
"ship" -> OrderState.Shipped("TRK-${System.currentTimeMillis()}")
"cancel" -> OrderState.Cancelled("Out of stock")
else -> state
}
is OrderState.Shipped -> when (action) {
"deliver" -> OrderState.Delivered(System.currentTimeMillis())
else -> state
}
is OrderState.Delivered -> state // terminal
is OrderState.Cancelled -> state // terminal
}
fun main() {
// Result
val result: Result<String> = Result.Success("Hello")
println(handleResult(result))
println(handleResult(Result.Error("Not found", 404)))
// Expression tree
val expr = Expr.Add(
Expr.Mul(Expr.Num(2.0), Expr.Num(3.0)),
Expr.Neg(Expr.Num(1.0))
)
println("${prettyPrint(expr)} = ${eval(expr)}") // (2.0 * 3.0) + (-1.0) = 5.0
// State machine
var order: OrderState = OrderState.Pending
order = transition(order, "process")
order = transition(order, "ship")
order = transition(order, "deliver")
println("Order state: $order")
}Use Cases
- Type-safe API response handling
- State machine implementations
- Expression trees and recursive data structures
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
kotlinadvanced
Generics — Variance, Bounds, and Type Projections
Master Kotlin generics: in/out variance, upper bounds, type projections, and generic constraints.
Best for: Type-safe generic APIs and containers
#kotlin#generics
kotlinbeginner
When Expression — Advanced Matching
Use when as expression and statement: type checks, ranges, destructuring, and guard conditions.
Best for: Exhaustive sealed class handling
#kotlin#when
kotlinintermediate
Value Classes — Zero-Cost Wrappers
Create type-safe wrappers with value classes: no runtime overhead, domain identifiers, and units.
Best for: Type-safe domain identifiers
#kotlin#value-class
kotlinbeginner
Value Classes and Type Aliases
Use value classes for type-safe wrappers without runtime overhead and type aliases for readability.
Best for: Type-safe domain primitives without runtime cost
#kotlin#value-class