Start learning free

LESSON 82 OF 348 · CONDITIONAL STATEMENTS

When Expressions in Kotlin: The Switch That Returns a Value

Lesson Read the full explanation

Other languages have switch. Kotlin has when — and it does more, because when is an expression: the whole block evaluates to the value of whichever branch matches, so you can assign the result directly to a variable. No temporary variable, no assignment repeated in every branch.

val score = 95
val grade = when (score) {
    in 90..100 -> "A"    // range check: 90 to 100, both ends included
    in 80..89 -> "B"
    else -> "F"
}
// grade is "A"

Each branch has the shape match -> result, and the match can be:

  • a plain value: 1 -> "one"
  • several values in one branch, comma-separated: 6, 7 -> "weekend"
  • a range: in 90..100 -> "A"
  • else — everything the branches above didn't catch

Branches are checked top to bottom and the first match wins, exactly like an if/else-if ladder — just far easier to read.


The trap:

The moment you USE the value of a when — assign it, return it — the compiler must be able to produce a value for every possible input. This is called being exhaustive. Forget the else:

val grade = when (score) {
    in 90..100 -> "A"
    in 80..89 -> "B"
}   // ...what should grade be when score is 42?

...and compilation stops with:

trap.kt:3:17: error: 'when' expression must be exhaustive. Add an 'else' branch.
    val grade = when (score) {
                ^^^^

Read it once, remember it forever: expression when ⇒ cover every case, usually by ending with else. (A when whose result you ignore is just a statement and doesn't need one.) This strictness is a gift — the compiler catches the "forgot a case" bug before your users do.


Assignment
Follow the Coding Tutorial and build two when expressions:

  • create val score = 87 and convert it to a letter grade: in 90..100 -> "A", in 80..89 -> "B", else -> "F"; print it,
  • create val day = 3 and classify it with comma-separated match values: days 1, 2, 3, 4, 5 are "weekday", days 6, 7 are "weekend", anything else is "invalid"; print the day and its type.

The exact expected output is:

grade = B
day = 3, type = weekday

Hint
Both when blocks end with an else branch — leave one out and you'll meet the exhaustiveness error from above.


STEP 1 OF 10
    Ctrl + Enter to check

    KOTLIN · CONDITIONAL STATEMENTS

    When Expressions in Kotlin, explained

    What when is

    Many languages have a switch statement. Kotlin has when, which does the same job and more, because when is an expression: the whole block evaluates to the value of the branch that matches, so you can assign it straight to a variable. There is no temporary variable and no assignment repeated in every branch.

    val score = 95
    val grade = when (score) {
        in 90..100 -> "A"    // range check: 90 to 100, both ends included
        in 80..89 -> "B"
        else -> "F"
    }
    // grade is "A"
    

    Each branch has the shape match -> result. The match can be:

    • a plain value: 1 -> "one";
    • several values in one branch, separated by commas: 6, 7 -> "weekend";
    • a range: in 90..100 -> "A";
    • else, which matches everything the branches above did not.

    Branches are checked from top to bottom and the first match wins, just like an if / else if chain, but with less to read. No break is needed, and a matching branch never falls through to the next one.

    Exhaustiveness: the rule to remember

    As soon as you use the value of a when, by assigning it or returning it, the compiler must be able to produce a value for every possible input. This is called being exhaustive. Leave out the else and this does not compile:

    val grade = when (score) {
        in 90..100 -> "A"
        in 80..89 -> "B"
    }   // what should grade be when score is 42?
    
    error: 'when' expression must be exhaustive. Add an 'else' branch.
    

    The compiler cannot know that score will always be between 80 and 100, so it asks for a final else. A when whose result you ignore is only a statement, and it does not need an else. The strictness is helpful: a forgotten case becomes a compile error instead of a bug your users find.

    More forms of when

    when without an argument

    You can leave out the value in parentheses and write full conditions in the branches. It then reads as a tidy replacement for a chain of else if:

    val t = 3
    val size = when {
        t < 2 -> "small"
        t < 5 -> "medium"
        else -> "big"
    }
    println(size)   // medium
    

    Order matters

    Because the first match wins, an overlapping branch placed earlier hides a later one. In when (87) { in 80..100 -> "high"; in 90..100 -> "top"; else -> "low" }, no number can ever reach the "top" branch. Put the narrower matches first.

    Common mistakes

    • Forgetting else when you use the result. This gives the exhaustiveness error above.
    • Writing || instead of commas. To match several values in a branch, write 1, 2, 3 ->. In a when with an argument, 1 || 2 is not a list of values.
    • Leaving out in before a range. 90..100 -> "A" compares score with the range object itself and does not check membership, and for an Int the compiler reports an incompatible type. Write in 90..100.
    • Forgetting that ranges are inclusive. 80..89 includes both 80 and 89. To exclude the end, use 80 until 90.
    • Mixing result types. The branches of one when should produce compatible values, such as all strings. A mixture gives the variable a wider type that is harder to use.

    The lesson's exercise

    The task builds two when expressions and prints this output:

    grade = B
    day = 3, type = weekday
    

    The letter grade

    1. Declare score as val score = 87. Kotlin infers Int.
    2. Open the when with val grade = when (score) { and add in 90..100 -> "A". The result of the matching branch flows into grade.
    3. Add in 80..89 -> "B". It is checked only after the A range fails to match. The score of 87 lands here.
    4. Close with else -> "F" and the closing brace, so that every possible score has an answer.
    5. Print it with a string template, "grade = $grade".

    The type of day

    1. Declare day as val day = 3.
    2. Write the weekday branch as 1, 2, 3, 4, 5 -> "weekday". One arrow covers all five values.
    3. Add the weekend branch, 6, 7 -> "weekend", and an else -> "invalid" for every other number.
    4. Print both variables in one template: "day = $day, type = $kind".
    val score = 87
    val grade = when (score) {
        in 90..100 -> "A"
        in 80..89 -> "B"
        else -> "F"
    }
    println("grade = $grade")
    val day = 3
    val kind = when (day) {
        1, 2, 3, 4, 5 -> "weekday"
        6, 7 -> "weekend"
        else -> "invalid"
    }
    println("day = $day, type = $kind")
    

    Change day to 42 and the last line prints day = 42, type = invalid, and a score of 42 gives grade = F. In both cases the else branch handles what the others don't.

    Key takeaways

    • when is an expression: it evaluates to the value of the matching branch.
    • Branches can match one value, several values separated by commas, a range with in, or anything with else.
    • The first matching branch wins, so order overlapping branches from narrow to broad.
    • If you use the result, the when must be exhaustive, which usually means ending with else.
    Keep going with KotlinAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free