Как работает when-выражение и чем оно отличается от switch?

В Kotlin when — это мощная альтернатива switch из языков вроде Java, но с гораздо более широкими возможностями и гибкостью. Оно используется как выражение (возвращает значение) или как инструкция (выполняет действия без возвращения значения). when превосходит switch практически во всём: синтаксисе, выразительности, безопасности и типизации.

1. Базовый синтаксис when

val result = when (x) {
1 -> "one"
2 -> "two"
else -> "unknown"
}

Аналог в Java:

switch (x) {
case 1: return "one";
case 2: return "two";
default: return "unknown";
}

2. when может быть без аргумента

Можно использовать when как if-else if-цепочку:

when {
x < 0 -> println("Negative")
x == 0 -> println("Zero")
else -> println("Positive")
}

3. when — это выражение

Оно возвращает значение, что позволяет сразу присвоить его переменной:

val message = when (score) {
in 90..100 -> "Excellent"
in 70..89 -> "Good"
in 0..69 -> "Try again"
else -> "Invalid"
}

В отличие от Java switch, который можно использовать только как оператор (не возвращает значение напрямую).

4. Поддержка любого типа, не только Int и String

when работает не только с числами и строками, но и с объектами, типами, булевыми выражениями и т.п.

val obj: Any = "Hello"
val type = when (obj) {
is String -> "String"
is Int -> "Integer"
else -> "Unknown"
}

Java switch до версии 14 поддерживал только int, char, enum, String.

5. Работа с диапазонами и коллекциями

when (x) {
in 1..10 -> println("From 1 to 10")
in validIds -> println("Valid ID")
!in 100..200 -> println("Outside range")
}

В Java switch нельзя проверять попадание в диапазон без явного условия в case.

6. Группировка значений

Можно группировать несколько значений через запятую:

when (x) {
0, 1 -> println("Zero or One")
2, 3, 4 -> println("Two to Four")
}

В Java switch приходится писать несколько case:

switch (x) {
case 0:
case 1:
System.out.println("Zero or One");
break;
}

7. else обязателен при использовании как выражения

Если when используется для присвоения значения, обязательно нужно указать else, чтобы покрыть все возможные случаи.

val label = when (value) {
"a" -> "Alpha"
"b" -> "Beta"
else -> "Unknown"
}

8. Можно выполнять блоки кода с фигурными скобками

Если нужно выполнить несколько операций в ветке:

when (command) {
"START" -> {
println("Starting...")
startProcess()
}
"STOP" -> {
println("Stopping...")
stopProcess()
}
}

9. Использование с enum

when отлично сочетается с enum и требует обработки всех возможных значений (если не указать else, компилятор покажет ошибку, если не все покрыты):

enum class Direction { NORTH, SOUTH, EAST, WEST }
fun move(dir: Direction): String = when (dir) {
Direction.NORTH -> "Up"
Direction.SOUTH -> "Down"
Direction.EAST -> "Right"
Direction.WEST -> "Left"
}

10. Вложенные when

Можно использовать when внутри when:

when (x) {
in 1..10 -> when (y) {
in 1..5 -> println("x=1..10, y=1..5")
else -> println("x=1..10, y>5")
}
else -> println("x > 10")
}

11. Сравнение с switch

Особенность when в Kotlin switch в Java
Возвращает значение ✅ Да (выражение) ❌ Нет (до Java 14)
--- --- ---
Работает с любыми типами ✅ Любые (Any) ❌ Только int, char, enum, String
--- --- ---
Диапазоны и коллекции ✅ Да (in, !in) ❌ Нет
--- --- ---
Группировка значений ✅ Через запятую ❌ Только множественные case
--- --- ---
Без аргумента ✅ Да ❌ Нет
--- --- ---
Обязательный else ✅ При выражении ❌ Не обязателен
--- --- ---
Вложенность ✅ Удобна ❌ Муторно
--- --- ---

12. Пример: расчёт налога

fun taxRate(income: Int): Double = when {
income <= 10_000 -> 0.0
income <= 50_000 -> 0.1
income <= 100_000 -> 0.2
else -> 0.3
}

Такой код в Java выглядел бы длиннее и менее выразительно.

13. Компилятор подсказывает про непокрытые случаи

Если when используется на sealed class или enum, и ты не покрыл все случаи — компилятор напомнит. Это даёт больше безопасности.

sealed class Animal
class Cat : Animal()
class Dog : Animal()
fun sound(animal: Animal): String = when (animal) {
is Cat -> "Meow"
is Dog -> "Woof"
// else не нужен, если все подклассы покрыты
}

when — это мощный инструмент, сочетающий в себе лаконичность, выразительность и безопасность, чего switch в Java не предоставляет в полной мере.