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
elsewhen you use the result. This gives the exhaustiveness error above. - Writing
||instead of commas. To match several values in a branch, write1, 2, 3 ->. In awhenwith an argument,1 || 2is not a list of values. - Leaving out
inbefore a range.90..100 -> "A"comparesscorewith the range object itself and does not check membership, and for anIntthe compiler reports an incompatible type. Writein 90..100. - Forgetting that ranges are inclusive.
80..89includes both 80 and 89. To exclude the end, use80 until 90. - Mixing result types. The branches of one
whenshould 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
- Declare
scoreasval score = 87. Kotlin infersInt. - Open the
whenwithval grade = when (score) {and addin 90..100 -> "A". The result of the matching branch flows intograde. - Add
in 80..89 -> "B". It is checked only after the A range fails to match. The score of 87 lands here. - Close with
else -> "F"and the closing brace, so that every possible score has an answer. - Print it with a string template,
"grade = $grade".
The type of day
- Declare
dayasval day = 3. - Write the weekday branch as
1, 2, 3, 4, 5 -> "weekday". One arrow covers all five values. - Add the weekend branch,
6, 7 -> "weekend", and anelse -> "invalid"for every other number. - 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
whenis 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 withelse. - The first matching branch wins, so order overlapping branches from narrow to broad.
- If you use the result, the
whenmust be exhaustive, which usually means ending withelse.