Условия и циклы: if, when, for, while

В Kotlin if и when — это выражения, которые возвращают значение, а циклы for удобно работают с диапазонами и коллекциями.
Суть: when заменяет громоздкий switch и цепочки if-else, а способность ветвлений возвращать значение делает код короче и выразительнее.

Главное отличие управляющих конструкций Kotlin от Java в том, что if и when — выражения. Они возвращают значение, поэтому ими можно прямо инициализировать переменную. Это убирает многословные конструкции, где сначала объявляли переменную, а потом присваивали её в ветках.

val age = 18
// if как выражение
val status = if (age >= 18) "взрослый" else "несовершеннолетний"
println(status)  // взрослый

Конструкция when

when — это улучшенный switch. Он сопоставляет значение с вариантами, поддерживает диапазоны, несколько значений через запятую и проверку типов. И, как if, тоже возвращает значение.

fun describe(x: Int): String = when {
    x < 0 -> "отрицательное"
    x == 0 -> "ноль"
    x in 1..9 -> "одна цифра"
    else -> "большое число"
}

println(describe(-5)) // отрицательное
println(describe(0))  // ноль
println(describe(7))  // одна цифра
println(describe(99)) // большое число

Циклы и диапазоны

Цикл for в Kotlin перебирает диапазоны и коллекции. Диапазон задаётся через .. (включая конец), until (исключая конец) или downTo (по убыванию). Шаг задаётся через step.

for (i in 1..5) print(i)        // 12345
println()
for (i in 0 until 5) print(i)   // 01234 (5 не входит)
println()
for (i in 10 downTo 1 step 2) print("$i ") // 10 8 6 4 2
println()

val fruits = listOf("яблоко", "груша", "слива")
for (f in fruits) println(f)

Как работает под капотом

Диапазон 1..5 — это объект IntRange, реализующий интерфейс перебора. Поэтому for по нему работает так же, как по списку. Конструкция when компилируется в эффективную таблицу переходов или в цепочку проверок в зависимости от условий. Когда when используется как выражение, компилятор требует покрыть все случаи (например, ветку else), иначе результат был бы неопределён.

  when (как выражение)
   значение --> [ветка 1] --> результат
            --> [ветка 2] --> результат
            --> [else]    --> результат
  Все ветки обязаны вернуть значение совместимого типа

Частые ошибки

Забывать else в when-выражении. Если when возвращает значение, компилятор требует исчерпывающего покрытия. Для enum и sealed-классов он умеет проверять полноту без else.

Путать .. и until. 1..5 включает 5, а 1 until 5 — нет. На индексах массива это частый источник ошибки на единицу.

Писать цепочки if-else там, где просится when. Длинные лестницы условий читаются хуже компактного when.

Best practices

  • Используйте if и when как выражения, инициализируя ими переменные напрямую.
  • Три и более ветвей — повод заменить if-else на when.
  • Для перебора индексов берите indices или withIndex(), а не ручной счётчик.
  • Помните разницу .. и until, чтобы не выйти за границы.

Логику when удобно прорепетировать на Python — классификация числа по веткам. Запустите врезку.

# Аналог when-выражения из Kotlin
def describe(x):
    if x < 0: return 'отрицательное'
    if x == 0: return 'ноль'
    if 1 <= x <= 9: return 'одна цифра'
    return 'большое число'

for v in [-5, 0, 7, 99]:
    print(v, '->', describe(v))

Попробуй сам ▶ — добавьте ветку для отрицательных однозначных чисел или для диапазона 10..99. В Kotlin это была бы новая строка в when.

Закрепим главное

Удержите в голове связку «выражение вместо инструкции»: в Kotlin ветвление не просто выбирает путь исполнения, а вычисляет значение. Это смещает стиль мышления — вместо «объяви переменную, потом в каждой ветке присвой её» вы пишете «переменная равна результату when». Меньше промежуточных состояний — меньше места для ошибки. Тот же подход вы встретите в Compose, где функция-экран по сути вычисляет описание UI из состояния через ветвления.

Второй ориентир — читаемость диапазонов. Диапазоны делают намерение явным: for (i in list.indices) честно говорит «перебираю индексы», а for ((index, value) in list.withIndex()) сразу даёт и позицию, и элемент. Эти идиомы вытесняют ручные счётчики и снимают целый класс ошибок выхода за границы. Привыкайте читать цикл как описание намерения, а не как механику инкремента.

Итог: ветвления-выражения и мощный when делают код короче и понятнее, а диапазоны превращают циклы в декларативные конструкции. Эти инструменты вы будете встречать в каждом экране Android-приложения.

Проверьте себя
1. Чем when в Kotlin выгодно отличается от обычного switch?
Awhen работает только с числами
Bwhen является выражением и может возвращать значение, поддерживает диапазоны, типы и наборы значений
Cwhen нельзя использовать без else никогда
Dwhen медленнее, но красивее
2. Чем отличаются диапазоны 1..5 и 1 until 5?
AНичем, это синонимы
B1..5 не включает 5, а 1 until 5 включает
C1..5 включает 5, а 1 until 5 не включает (идёт до 4)
D1 until 5 идёт в обратном порядке