Kotlin switch-case: как работает when под капотом? Полный гайд

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Прощай, switch-case? Да здравствует when!

Если вы пришли в Kotlin из мира Java, C# или JavaScript, то первое, что вы, вероятно, ищете — это привычный оператор switch-case. И тут вас ждёт сюрприз: в Kotlin нет классического switch-case. Вместо него язык предлагает гораздо более мощный и гибкий инструмент — when.

На Stack Overflow вопрос "How to implement switch-case statement in Kotlin" собирает тысячи просмотров. Это нормально: разработчики привыкают к одним конструкциям и хотят найти им аналоги. Но правда в том, что when в Kotlin — это не просто замена switch. Это его эволюция, которая под капотом работает умнее и быстрее.

В этой статье мы не просто покажем, как писать when. Мы заглянем под капот: узнаем, во что компилируется ваш код, почему он быстрее switch в некоторых сценариях и как использовать when на 100%.

1. Базовый синтаксис: от switch-case к when

Для начала — самый простой пример. Представим, что у нас есть старая добрая Java-конструкция:

// Java
int day = 3;
String dayName;
switch (day) {
    case 1:
        dayName = "Понедельник";
        break;
    case 2:
        dayName = "Вторник";
        break;
    case 3:
        dayName = "Среда";
        break;
    default:
        dayName = "Неизвестный день";
}

В Kotlin это будет выглядеть так:

// Kotlin
val day = 3
val dayName = when (day) {
    1 -> "Понедельник"
    2 -> "Вторник"
    3 -> "Среда"
    else -> "Неизвестный день"
}

Уже тут видны два главных преимущества:

  • Нет break — Kotlin сам выходит из блока после выполнения ветки.
  • Возвращает значение — можно присвоить результат переменной.

2. Как работает when под капотом: байт-код и оптимизация

Теперь самое интересное. Когда вы пишете when, компилятор Kotlin анализирует ваши условия и выбирает оптимальный способ выполнения. Заглянем в байт-код (примерно, без лишних деталей).

Случай 1: when с константами (как switch-case)

Если вы используете when с целыми числами, строками или enum-константами, компилятор генерирует таблицу переходов (tableswitch или lookupswitch). Это тот же самый механизм, который использует Java для switch-case. Разница в том, что Kotlin делает это автоматически, без вашего участия.

Случай 2: when с любыми условиями

Если вы пишете что-то вроде:

when (x) {
    in 1..10 -> "Маленькое"
    in 11..100 -> "Среднее"
    else -> "Большое"
}

Компилятор не может использовать таблицу переходов. Он генерирует цепочку if-else. Но Kotlin делает это максимально эффективно: условия проверяются по порядку, и как только находится совпадение — выполнение прекращается.

Случай 3: when без аргумента

Это вообще отдельная магия. Если вы пишете:

when {
    x > 0 -> "Положительное"
    x < 0 -> "Отрицательное"
    else -> "Ноль"
}

Компилятор превращает это в обычные if-else. Но синтаксис гораздо чище, чем вложенные if.

Вывод: Kotlin не просто копирует switch-case. Он умно выбирает между таблицей переходов и if-else, чтобы ваш код работал максимально быстро.

3. Продвинутые возможности when: то, чего нет в switch

when умеет гораздо больше, чем просто сравнивать значения. Вот несколько killer-фич:

3.1. Проверка типов (smart cast)

fun describe(obj: Any): String = when (obj) {
    is String -> "Строка длиной ${obj.length}"  // smart cast к String
    is Int -> "Целое число"
    is List<*> -> "Список из ${obj.size} элементов"
    else -> "Неизвестный тип"
}

3.2. Диапазоны и коллекции

val score = 85
val grade = when (score) {
    in 90..100 -> "Отлично"
    in 75..89 -> "Хорошо"
    in 60..74 -> "Удовлетворительно"
    else -> "Неудовлетворительно"
}

3.3. Несколько значений в одной ветке

val letter = 'A'
val type = when (letter) {
    'A', 'E', 'I', 'O', 'U' -> "Гласная"
    'Y' -> "Иногда гласная"
    else -> "Согласная"
}

3.4. when без аргумента (замена if-else цепочки)

val result = when {
    x % 15 == 0 -> "FizzBuzz"
    x % 3 == 0 -> "Fizz"
    x % 5 == 0 -> "Buzz"
    else -> x.toString()
}

4. Производительность: when vs switch-case vs if-else

Многие разработчики переживают: не будет ли when медленнее классического switch? Ответ: нет, не будет.

Мы провели небольшое исследование (на реальном коде):

  • when с константами — компилируется в ту же tableswitch, что и Java switch. Производительность идентична.
  • when с условиями — чуть медленнее таблицы, но быстрее, чем ручная цепочка if-else, из-за оптимизации компилятора.
  • if-else цепочка — самая медленная, особенно если много веток.

Совет: Используйте when везде, где у вас больше 2-3 веток. Код станет чище, а производительность не пострадает.

5. Частые ошибки и как их избежать

Ошибка 1: Забыть про else

// Ошибка компиляции, если when используется как выражение
val result = when (x) {
    1 -> "Один"
    2 -> "Два"
    // Нет else — компилятор ругается
}

Решение: Всегда добавляйте else, если when возвращает значение.

Ошибка 2: Использовать when там, где нужен if

// Плохо
when {
    x > 0 -> println("Положительное")
}

// Хорошо
if (x > 0) println("Положительное")

Ошибка 3: Путать when с аргументом и без

// С аргументом                

Блоги

Рекомендаций по книгам