Всем привет, меня зовут Сергей Прощаев. Я техлид в FinTech и преподаю на курсах в Otus. Мы продолжаем наше погружение в Kotlin.

В прошлый раз мы говорили о типах данных и переменных. Это был фундамент. Но данные без действий мертвы. Самое интересное начинается, когда программе нужно принимать решения: если клиент VIP, дать ему кэшбэк 5%, иначе — 1%. Или повторять действие: отправлять запрос на сервер, пока не получим ответ.

Знаете, в чём беда многих новичков? Они знают синтаксис if и for, но пишут такие «спагетти», что через месяц сами не могут разобраться. Сегодня мы не просто пройдем тему «Условные операторы и циклы». Мы разберем, как управлять ходом программы правильно, красиво и эффективно. Чтобы ваш код читался как хорошая книга, а не как шифровка.

Движемся дальше!

1. Булева вселенная: Boolean и сравнение

Всё начинается с истины или лжи. Тип Boolean (true/false) — это основа любого условия.

Вспомним прошлую статью:

val isEnabled = true
val hasAccess = false

Но чаще мы получаем Boolean в результате сравнений. Тут всё привычно, но есть нюанс.

fun main() {
    val a = 10
    val b = 20

    println(a > b)  // false
    println(a < b)  // true
    println(a >= 10) // true
    println(a == 10) // true
    println(a != b)  // true
}

Важный момент для тех, кто пришел из Java или других языков. В Kotlin есть два вида равенства:

  • == — структурное равенство (проверяет содержимое). В Java для этого нужно писать equals().

  • === — ссылочное равенство (проверяет, указывают ли переменные на один и тот же объект в памяти).

Для базовых типов (числа, строки) это редко нужно, но когда дойдете до классов, запомните: в 99% случаев вы будете использовать ==. Компилятор Kotlin сам под капотом вызовет equals.

2. if — это не просто оператор. Это выражение

И вот тут начинается магия Kotlin, которая бесит Java‑разработчиков на код‑ревью (в хорошем смысле).

В Java if — это инструкция (statement). Она ничего не возвращает.

В Kotlin if — это выражение (expression). Оно возвращает значение!

Смотрите, как мы обычно писали в Java:

// Java-style
int max;
if (a > b) {
    max = a;
} else {
    max = b;
}

А теперь, как это делается в Kotlin:

fun main() {
    val a = 15
    val b = 20

    val max = if (a > b) a else b
    println("Максимум: $max") // Максимум: 20
}

Лаконично, правда? Это полностью заменило тернарный оператор ? :, который многие не любят из‑за сложности чтения.

Когда это спасает? Представьте, что мы считаем комиссию за перевод. Если сумма больше 1000, комиссия фиксированная, если меньше — процентная.

fun calculateFee(amount: Double): Double {
    return if (amount >= 1000.0) {
        println("Применяем фиксированную комиссию")
        50.0 // Последнее выражение в блоке — это результат if
    } else {
        println("Применяем процентную комиссию")
        amount * 0.02
    }
}

Код стал декларативным. Мы говорим: «комиссия равна — если сумма больше 1000, то 50, иначе 2%». Читать такой код — одно удовольствие.

3. Визуализируем поток

Чтобы закрепить, как работает ветвление, давайте посмотрим на простую блок‑схему, представленную на рисунке 1. Допустим, у нас есть логин: если имя «Admin», пускаем, если нет — проверяем пароль.

Рисунок 1. Типичный flow авторизации. Каждый ромб — это условие, каждый прямоугольник — действие.
Рисунок 1. Типичный flow авторизации. Каждый ромб — это условие, каждый прямоугольник — действие.

Таких ромбов в коде могут быть десятки. И чем их больше, тем важнее писать их красиво. Для этого в Kotlin есть король.

4. when — швейцарский нож управляющих конструкций

Забудьте про switch-case. В Kotlin есть when, и он в сто раз мощнее.

Пример 1: Простая замена if-else if

Допустим, мы обрабатываем HTTP‑статусы.

fun handleHttpStatus(code: Int): String {
    return when (code) {
        200 -> "OK"
        404 -> "Not Found"
        500 -> "Internal Server Error"
        else -> "Unknown Status"
    }
}

Чисто, красиво, без break.

Пример 2: Проверка на вхождение в диапазон (об этом чуть позже)

fun getTemperatureCategory(celsius: Int): String {
    return when (celsius) {
        in -50..0 -> "Морозно"
        in 1..15 -> "Прохладно"
        in 16..30 -> "Тепло"
        in 31..50 -> "Жарко"
        else -> "Экстремально"
    }
}

Пример 3: Проверка типа (пригодится, когда изучите наследование)

fun printType(obj: Any) {
    when (obj) {
        is String -> println("Это строка длиной ${obj.length}")
        is Int -> println("Это число: $obj")
        else -> println("Что-то другое")
    }
}

Пример 4: when без аргумента (как замена цепочке if)

Иногда нужно проверить сложные условия.

fun getDiscount(customerType: String, years: Int, amount: Double): Double {
    return when {
        customerType == "vip" && years > 5 -> 0.20
        customerType == "vip" -> 0.15
        amount > 10000 -> 0.10
        years > 3 -> 0.05
        else -> 0.0
    }
}

Видите? Мы убрали лесенку из if-else. Код читается как таблица правил. Именно так мы и пишем логику в наших FinTech‑сервисах — это best practice.

5. Диапазоны (Ranges): синтаксический сахар, который вы полюбите

Мы уже использовали in 1..10. Давайте разберем, что это такое.

Диапазон (IntRange) — это просто объект, который представляет интервал от и до.

val oneToTen = 1..10
val tenToOne = 10 downTo 1
val evenNumbers = 0..100 step 2
val letters = 'A'..'Z'

fun main() {
    println(5 in oneToTen) // true
    println(15 in oneToTen) // false
    println('C' in letters) // true
}

Зачем это нужно? Кроме проверок в when, это незаменимо в циклах.

6. Циклы: повторение — мать учения

for по диапазонам и коллекциям

В Kotlin нет классического for (int i = 0; i < 10; i++). Вместо этого — итерация по диапазонам.

fun main() {
    // Вывести числа от 1 до 5
    for (i in 1..5) {
        println("Индекс: $i")
    }

    // Вывести числа от 5 до 1 (обратный порядок)
    for (i in 5 downTo 1) {
        println("Обратный: $i")
    }

    // Перебор с шагом
    for (i in 0..10 step 2) {
        println("Четное: $i")
    }

    // Если нужно перебрать массив/список и получить индексы
    val fruits = listOf("Apple", "Banana", "Orange")
    for (index in fruits.indices) {
        println("$index -> ${fruits[index]}")
    }

    // Лучший способ — сразу получить и индекс и значение (реальная практика)
    for ((index, fruit) in fruits.withIndex()) {
        println("$index -> $fruit")
    }
}

while и do‑while

Работают классически. Используем, когда количество итераций неизвестно.

fun main() {
    var x = 5
    while (x > 0) {
        println("Осталось: $x")
        x--
    }

    var input: String?
    do {
        print("Введите 'exit' для выхода: ")
        input = readlnOrNull()
    } while (input != "exit")
}

repeat — простая вещь, которую многие не знают

Нужно просто повторить действие N раз? Забудьте про for i in 1..N.

import kotlin.repeat

fun main() {
    repeat(3) { iterationNumber ->
        println("Повторение номер $iterationNumber") // iterationNumber идет от 0
    }
}

Идеально для тестовых заглушек или простых утилит.

7. Джампы: return, break, continue и их метки

Это та тема, где новички часто теряются, особенно когда есть вложенные циклы.

  • return — выходит из функции.

  • break — прерывает текущий цикл.

  • continue — переходит к следующей итерации цикла.

Проблема: как выйти из внешнего цикла, находясь во внутреннем?

В Java пришлось бы использовать флаг found = true;. В Kotlin для этого есть метки (labels).

fun main() {
    outer@ for (i in 1..3) {
        for (j in 1..3) {
            if (i == 2 && j == 2) {
                println("Нашли! Выходим из внешнего цикла на метке outer")
                break@outer // Выходим из цикла, помеченного outer
            }
            println("i=$i, j=$j")
        }
    }
    println("Готово")
}

Вывод будет:

i=1, j=1
i=1, j=2
i=1, j=3
i=2, j=1
Нашли! Выходим из внешнего цикла на метке outer
Готово

Без метки break просто прервал бы внутренний цикл, и внешний продолжился бы с i=3.

8. Реальная история: Как мы перестали писать простыни из if‑else и сэкономили 10% времени

Расскажу случай, который произошел у нас в команде, когда мы переписывали скоринговый модуль (оценка кредитоспособности клиента).

Исходный код на Java был написан 5 лет назад. Там был метод calculateRiskScore, который представлял собой монстра строк на 300. Это была бесконечная лестница if-else:

if (client.getAge() > 60 && client.hasPension()) {
    // ... 20 строк логики
} else if (client.getAge() > 60) {
    // ... еще 15 строк
} else if (client.getIncome() > 100000 && client.getJob().isStable()) {
    // ... ад
}
// и так далее

Добавить новое правило или изменить вес фактора было страшно. Ошибка в одном условии могла привести к тому, что мы выдали бы кредит мошеннику или отказали хорошему клиенту.

Когда мы переписывали это на Kotlin, мы применили два подхода, о которых я говорил сегодня:

  1. Мы заменили лестницу if-else на when без аргумента. Каждое правило стало одной строкой в when, вызывающей отдельную функцию расчета.

  2. Для проверок на вхождение (например, возраст в диапазоне 25–40) мы использовали in 25..40. Код стал читаться как техническое задание.

В итоге:

  • Размер метода сократился на порядок.

  • Сократилось время на код‑ревью.

  • А главное — мы нашли пару багов в старой логике (один else if был не на том месте), которые жили в проде годами. Компилятор Kotlin, конечно, не нашел бы логическую ошибку, но код стал настолько прозрачным, что ошибки бросились в глаза сами.

Это и есть сила выразительного синтаксиса.

Что дальше?

Мы разобрали мозг программы — условия и циклы. Теперь вы можете писать осмысленные алгоритмы. Но чтобы код не превращался в полотно, нужно учиться группировать действия. В следующей статье мы поговорим о функциях — как создавать свои строительные блоки и не повторяться.

Весь код из этой статьи доступен в моём репозитории на GitHub

Пишете на Kotlin, но в реальных задачах начинаете «плыть»: условия разрастаются, логика ломается, код становится трудно читать и менять?

Вам нужно не знать синтаксис, а уверенно писать предсказуемый и поддерживаемый код под реальные кейсы. Курс «Kotlin Developer. Basic» закрывает эту задачу: систематизирует базу и учит применять её так, чтобы код выдерживал рост сложности, а не превращался в спагетти.

Если хотите расти в Kotlin не урывками, а по понятной траектории — в OTUS есть каталог курсов по программированию, которые помогают перейти от базы к более уверенной практике.


Серия статей «Kotlin для новичков»: