When you use `when` as an expression over a sealed type and cover every subtype, why don't you need an `else` branch?
answer
- Closed subtype set = compiler can enumerate cases
- when as expression must be exhaustive
- All branches covered → no else
- Adding a subtype breaks compile (good)
- Each is-arm smart-casts the subject
basics
~10 sBecause the sealed type lists all its possible subtypes in advance. If you handle each one, the compiler knows nothing else is possible, so an else branch would be pointless.
solid answer
~40 sA `sealed class` or `sealed interface` has a fixed, closed set of direct subtypes known at compile time (all in the same module/package). When you use `when` as an *expression* (its result is used or returned) over such a type and provide a branch for every subtype, the compiler proves the `when` is exhaustive and accepts it without an `else`. This is better than `else` because if you later add a new subtype, the `when` stops compiling, forcing you to handle the new case. Note: `when` used as a *statement* (result ignored) over a sealed type is also checked for exhaustiveness since Kotlin 1.7. Each branch also smart-casts the subject to the matched subtype.
code
kotlin · 8 linessealed interface Result
data class Ok(val value: Int) : Result
data class Err(val msg: String) : Result
fun describe(r: Result): String = when (r) {
is Ok -> "value=${r.value}" // smart-cast to Ok
is Err -> "error=${r.msg}" // smart-cast to Err
} // exhaustive, no elsego deeper
Knows that covering all subtypes lets you drop else and that each branch smart-casts.
Explains the maintenance benefit — adding a subtype becomes a compile error rather than a silent runtime fall-through.
Distinguishes expression vs statement exhaustiveness and the 1.7 change; argues against defensive else on closed hierarchies.
Frames it as compiler-enforced totality over an ADT, a deliberate design lever to make illegal states unrepresentable across a codebase.
## What "sealed" means A `sealed class` or `sealed interface` declares a **closed hierarchy**: the complete set of direct subtypes is fixed and known to the compiler. All subtypes must live in the same module and package (historically the same file). Outside code cannot add new subtypes. ## Expression vs statement - A **`when` expression** is one whose value is *used* — assigned to a variable, returned, or passed as an argument. - A **`when` statement** ignores the result. When a `when` is used as an expression, Kotlin **requires** it to be exhaustive: every possible input must be matched. For arbitrary types you satisfy this with an `else` branch. ## Why no `else` for a sealed type Because the subtype set is closed, the compiler can enumerate every case. If your branches cover all direct subtypes, the `when` is **provably exhaustive** and you may omit `else`. ```kotlin sealed interface Shape data class Circle(val r: Double) : Shape data class Square(val side: Double) : Shape fun area(s: Shape): Double = when (s) { // expression: returned is Circle -> Math.PI * s.r * s.r // s smart-cast to Circle is Square -> s.side * s.side // s smart-cast to Square } // no else needed ``` ## The maintenance payoff If you add `data class Triangle(...) : Shape`, the `when` above **fails to compile** with "`when` expression must be exhaustive". An `else -> error(...)` would have silently swallowed the new case at runtime. So omitting `else` turns adding a variant into a compile-time checklist. ## Statement exhaustiveness (Kotlin 1.7+) Since Kotlin 1.7, a `when` *statement* over a sealed type (or enum/Boolean) also gets an exhaustiveness **warning/error** so you don't lose the check just by ignoring the result. ## Smart-casting Inside each `is` arm the subject is automatically **smart-cast** to that subtype, so you can read its properties without an explicit cast.
- What happens to this `when` if a teammate adds a new subtype of the sealed interface?The `when` expression no longer compiles — it reports it must be exhaustive — forcing you to add a branch for the new subtype.
Like a multiple-choice question where every option is printed: once you tick each option you don't need an 'other' box.
saying these in an interview costs you the question
- Says you always need an `else` even for sealed types
- Thinks `else` is preferable for safety (it actually hides new variants)
- Confuses sealed with open classes
- Doesn't know exhaustiveness only applies to `when` used as an expression (pre-1.7)