Sealed Classes
In this page:
Declaring a Sealed Class
sealed class restricts inheritance to a known, closed set of subclasses, which lets the compiler reason completely about every possible subtype at compile time.
Example: Declaring a Sealed Class
sealed class Result
data class Success(val data: String) : Result()
data class Failure(val error: String) : Result()
fun main() {
val result: Result = Success("Loaded")
println(result)
}
Login to try C/C++/Java/PHP code in the editor
Exhaustive when Without else
Because the compiler knows every subclass of a sealed class, a when expression covering all of them does not need an else branch and still compiles safely.
Example: Exhaustive when Without else
sealed class Result
data class Success(val data: String) : Result()
data class Failure(val error: String) : Result()
fun describe(result: Result): String = when (result) {
is Success -> "Success: ${result.data}"
is Failure -> "Failure: ${result.error}"
}
fun main() {
println(describe(Success("Data loaded")))
println(describe(Failure("Timeout")))
}
Login to try C/C++/Java/PHP code in the editor
Sealed Classes for Modeling States
Sealed classes are a great fit for representing a small, fixed set of states that each need to carry different data, such as loading states in an application.
Example: Sealed Classes for Modeling States
sealed class LoadState
object Loading : LoadState()
data class Loaded(val items: Int) : LoadState()
data class Error(val message: String) : LoadState()
fun render(state: LoadState): String = when (state) {
is Loading -> "Loading..."
is Loaded -> "Loaded ${state.items} items"
is Error -> "Error: ${state.message}"
}
fun main() {
println(render(Loading))
println(render(Loaded(10)))
}
Login to try C/C++/Java/PHP code in the editor
Sealed Classes Catch Missed Cases
If a new subclass is later added to a sealed class, any exhaustive when expression elsewhere that handled every existing case will fail to compile until it handles the new one too, catching missed logic early.
Note: If you added a Triangle subclass here, this when would fail to compile until a Triangle branch was added.
Example: Sealed Classes Catch Missed Cases
sealed class Shape
data class Circle(val radius: Double) : Shape()
data class Square(val side: Double) : Shape()
fun area(shape: Shape): Double = when (shape) {
is Circle -> Math.PI * shape.radius * shape.radius
is Square -> shape.side * shape.side
}
fun main() {
println("Circle area: ${area(Circle(2.0))}")
println("Square area: ${area(Square(3.0))}")
}
Login to try C/C++/Java/PHP code in the editor
- Forgetting a sealed class's subclasses must all live in the same module (and, before certain Kotlin versions, the same file/package), causing a compile error if placed elsewhere.
- Adding an unnecessary
elsebranch in awhenover a sealed class, hiding the compiler's ability to warn you when a new subtype is added later. - Confusing a sealed class with an enum; sealed subclasses can each carry their own different data, unlike a plain enum's fixed constant set.
sealed classrestricts which classes can subclass it, and all direct subclasses must be known to the compiler at compile time.- A
whenexpression over a sealed class's subtypes can be exhaustive without anelsebranch, since the compiler knows every possibility. - Sealed classes are ideal for representing a fixed set of related, but structurally different, states or results.
- Adding a new subclass later causes any exhaustive
whenblocks to fail to compile until they handle the new case too.
Chapter Quiz — Complete all 7 topics to unlock
0/7 topics done
Complete these topics first: