What you'll learn
Enums and sealed classes both model "one of a fixed set of possibilities" — but at different levels. Together with when, they let you model states and results in a way the compiler checks for completeness. This is one of Kotlin's real superpowers.
By the end you'll be able to:
- Define enum classes, including ones with properties and methods
- Model restricted hierarchies with sealed classes
- Write exhaustive
whenexpressions the compiler verifies - Know when to reach for an enum vs a sealed class
Enum classes
An enum class defines a fixed set of named constants — days of the week, directions, states. It gives you type safety (only valid values compile) and useful built-ins like ordinal and entries:
enum class Direction {
NORTH, SOUTH, EAST, WEST
}
val d = Direction.NORTH
println(d) // NORTH
println(d.ordinal) // 0 (its position)
println(Direction.entries.size) // 4 (all values)Enums with data & methods
Kotlin enums are more than labels — each constant can carry data and the enum can have methods. This makes them ideal for lookup tables with behaviour attached:
enum class Planet(val gravity: Double) {
EARTH(9.81),
MARS(3.71),
MOON(1.62); // note the semicolon before members
fun weightOf(mass: Double) = mass * gravity
}
println(Planet.MARS.gravity) // 3.71
println(Planet.EARTH.weightOf(10.0)) // 98.1Sealed classes
An enum's values are all identical in shape. But often you need a fixed set of possibilities where each carries different data — a Success holds a result, an Error holds a message. That's a sealed class: a restricted hierarchy where all subclasses are known to the compiler:
enum
A fixed set of single instances (constants). Each value is the same shape.
Direction.NORTH, SOUTH…
sealed
A fixed set of subtypes, each able to hold its own data. Perfect for results and state.
Success(data), Error(msg), Loading
The payoff: exhaustive when
Because the compiler knows every subclass of a sealed class, a when over one can be checked for exhaustiveness — no else branch needed. And if you add a new subtype later, the compiler forces you to handle it everywhere. A whole class of bugs, gone:
sealed class Result
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
object Loading : Result()
fun handle(result: Result) = when (result) { // no 'else' needed — exhaustive!
is Success -> "Got: ${result.data}"
is Error -> "Failed: ${result.message}"
Loading -> "Loading..."
}
println(handle(Success("Hello")))
println(handle(Error("Oops")))
println(handle(Loading))Key idea
Result pattern — Success/Error/Loading as a sealed hierarchy — is everywhere in real Kotlin, especially Android. It models an operation's state safely, and the exhaustive when guarantees you never forget to handle a case.Recap & quick check
Key takeaways
- An enum class is a fixed set of named constants; each value can carry data and the enum can have methods.
- A sealed class is a fixed set of subtypes, each able to hold its own different data.
- The compiler knows all subclasses of a sealed class, so a when over it can be exhaustive (no else).
- Add a new sealed subtype and the compiler forces you to handle it in every when — safety at scale.
- Use enums for identical constants; sealed classes for a closed set of differently-shaped cases (results, state).
Quick check
1. What is an enum class?
2. What can Kotlin enum constants carry?
3. What's the key benefit of a sealed class with 'when'?
4. When should you use a sealed class instead of an enum?
5. If you add a new subtype to a sealed class, what happens to existing exhaustive 'when' blocks?
Excellent — you can model states and results safely now. Next up: Module 19 — Visibility, Nested & Inner Classes, the finale of Phase 3.