skip to content

Explain Kotlin's when as an expression: its forms, exhaustiveness rules, and how it replaces switch.

level: middleimportance: must knowfreq 78%

answer

  1. Two forms: with subject vs subject-less (if/else-if)
  2. Conditions: values, commas, in ranges, is types
  3. Expression when must be exhaustive (or else)
  4. Sealed/enum: compiler checks exhaustiveness, no else needed
  5. Top-down, no fall-through, no break

basics

~10 s

when is Kotlin's flexible replacement for switch. It can return a value, match ranges or types, and needs to cover every case (be exhaustive) when used as an expression.

solid answer

~40 s

`when` is an **expression** (and also usable as a statement) that generalizes `switch`. It has two forms: with a subject — `when (x) { ... }` — and without one, where each branch is a boolean condition (`when { a > 0 -> ... }`), effectively an if/else-if chain. Branch conditions can be literals, comma-separated values, `in` range/collection checks, and `is` type checks (which trigger smart casts). `else` is the catch-all. When `when` is used as an **expression**, it must be **exhaustive**: cover all cases or include `else`. For sealed classes and enums the compiler can verify exhaustiveness without `else`, and a non-exhaustive `when` over them is now a compile error. Branches are evaluated **top-down**, and there is no fall-through, so no `break` is needed.

code

kotlin · 6 lines
kotlin
fun classify(x: Any): String = when (x) {
    in 1..9 -> "single digit"
    is String -> "len=${x.length}"   // smart cast to String
    null -> "null"
    else -> "other"
}

go deeper

for a junior

Knows when replaces switch and can return a value; uses else.

for a middle

Explains both forms, in/is conditions, and that expression-form when must be exhaustive.

for a senior

Leverages compiler-checked exhaustiveness over sealed/enum types and avoids a redundant else to keep checks active.

for a principal

Uses sealed hierarchies + exhaustive when as a domain-modeling tool so the compiler enforces total handling across the codebase.

## when replaces switch `when` is Kotlin's multi-way branch. Unlike Java's `switch`, it is an **expression** (returns a value), supports rich conditions, and has **no fall-through** (no `break` needed). ### Form 1 — with a subject ```kotlin val label = when (code) { 0 -> "zero" 1, 2, 3 -> "small" // comma = multiple matches in 4..9 -> "mid" // range check via in is String -> "text" // type check via is (smart-casts code) else -> "other" } ``` Branch conditions can be: constant values, comma-separated alternatives, `in`/`!in` for ranges and collections, and `is`/`!is` for type checks (with smart casts inside the branch). ### Form 2 — without a subject Omit the subject and each branch becomes a boolean condition — an if/else-if chain: ```kotlin val sign = when { n > 0 -> "+" n < 0 -> "-" else -> "0" } ``` ## Exhaustiveness When used as an **expression** (its value is assigned/returned), `when` must be **exhaustive** — every possible input is handled, or an `else` is present. - For **enums** and **sealed** hierarchies the compiler knows all cases, so listing them all makes `else` unnecessary — and it will warn/error if you add a case later and forget to handle it. - A non-exhaustive `when` expression over a sealed type or enum is a **compile error**. ```kotlin sealed interface Shape class Circle : Shape class Square : Shape fun area(s: Shape): String = when (s) { // no else needed is Circle -> "pi r^2" is Square -> "a^2" } ``` Used as a pure **statement**, exhaustiveness is not enforced (you may omit `else`). ## Evaluation semantics - Branches are checked **top to bottom**; the first match wins. - There is **no fall-through**, so no `break`. - A subject can be captured: `when (val r = compute()) { ... }`. ## Why this matters Enforced exhaustiveness over sealed types turns "forgot a case" into a compile-time error, which is a core reason teams model domains with sealed hierarchies.

  • Why prefer a sealed class with when over an enum-plus-else?
    With a sealed class, an exhaustive when has no else, so adding a new subtype forces a compile error in every when that didn't handle it — the compiler finds all the spots you must update.
  • Does when fall through like a C switch?
    No. The first matching branch runs and execution leaves the when; there is no fall-through and no break keyword.

saying these in an interview costs you the question

  • Adding break inside when branches
  • Putting a catch-all else on a sealed when, defeating exhaustiveness checks
  • Not knowing the subject-less form exists
  • Thinking when only matches constants like switch
  • Believing exhaustiveness is checked even for statement-form when

context