1. Введение
Если вы только что освоили if, то мозг пока воспринимает его как «перекрёсток»: если условие истинно — идём налево и что-то делаем, иначе — направо и тоже что-то делаем. И это нормально. Но в реальных программах нам часто нужно не только «сделать действие», а получить результат выбора: строку, число, метку, которую мы потом используем дальше.
Представьте, что вы выбираете, какую табличку повесить на дверь: "Открыто" или "Закрыто". Это не отдельное действие ради действия — это значение, которое нужно сохранить и потом вывести. Kotlin в этом месте говорит: «Окей, пусть if сразу возвращает табличку». И это очень удобно.
if как оператор и if как выражение
Когда мы пишем if «по-старому», он работает как оператор (statement): выполняет или не выполняет кусок кода, и на этом всё.
Но Kotlin разрешает использовать if как выражение (expression), то есть как штуку, у которой есть значение, как у 3 + 4 или у "Hi".length. Это официальная часть языка: if можно писать как выражение и присваивать его результат в переменную, но тогда ветка else становится обязательной.
Чтобы уловить идею, давайте сравним два стиля — «сначала завёл коробку (var), потом положил туда результат» и «сразу получил результат».
Вот стиль, который вы уже умеете (операторный if):
fun main() {
val temperature = 28
var label = "Нормально"
if (temperature > 25) {
label = "Жарко"
}
println(label) // Жарко
}
Здесь if ничего «не возвращает». Мы просто меняем label.
А вот стиль, который мы хотим освоить сегодня (выражение if):
fun main() {
val temperature = 28
val label = if (temperature > 25) "Жарко" else "Нормально"
println(label) // Жарко
}
Разница по смыслу такая: во втором варианте мы прямо говорим Kotlin: «label — это результат выбора».
Базовый синтаксис: val x = if (cond) A else B
Базовая форма такая: val result = if (условие) значение1 else значение2. Она читается почти как человеческая фраза: «result равно (если условие, то значение1, иначе значение2)».
val result = if (condition) value1 else value2
if как выражение
Пример, который всегда хорошо заходит — модуль числа (абсолютное значение). В математике это |x|, а в коде мы просто выбираем: либо x, либо -x.
fun main() {
val x = -7
val abs = if (x >= 0) x else -x
println("abs = $abs") // abs = 7
}
Обратите внимание: здесь мы вообще не используем var. Это маленькая, но важная победа: меньше изменяемых переменных — меньше мест, где можно «случайно всё сломать».
Почему else обязателен
С точки зрения новичка требование «почему я не могу написать val r = if (cond) 1?» кажется придиркой. Но компилятор не вредничает, он заботится о логике: если вы присваиваете результат, то результат должен существовать всегда, при любом исходе условия.
В Kotlin прямо сказано: если if используется как выражение, ветка else обязательна.
Плохой пример (так нельзя):
fun main() {
val isVip = true
val discount = if (isVip) 10 // Ошибка компиляции: нет else
}
Хороший пример (так правильно):
fun main() {
val isVip = true
val discount = if (isVip) 10 else 0
println("discount = $discount") // discount = 10
}
В этом месте удобно запомнить правило: если if стоит справа от = и вы хотите сохранить результат, то else всегда обязателен.
Ветки-блоки { ... }: значение — это последнее выражение
Часто в ветке нужно сделать не одну вещь: например, распечатать пояснение и только потом вернуть значение. Kotlin разрешает, чтобы каждая ветка была блоком { ... }. Тогда результатом ветки становится последнее выражение в блоке.
Сделаем мини-пример: выбираем, кого считать «выше» — Алису или Боба, и при этом печатаем, кого выбрали (как будто мы ведём лог).
fun main() {
val heightAlice = 160
val heightBob = 175
val taller = if (heightAlice > heightBob) {
println("Выбрали Алису") // Выбрали Алису (если бы условие было true)
heightAlice
} else {
println("Выбрали Боба") // Выбрали Боба
heightBob
}
println("taller = $taller") // taller = 175
}
Заметьте «фокус»: println(...) — это не значение для taller. Значение — это последняя строка ветки (heightAlice или heightBob). Это очень важная привычка: в конце ветки if-выражения должно стоять то, что вы хотите вернуть.
Маленькая схема, как это работает
flowchart TD
A["if (условие)"] -->|true| B["ветка then { ... }"]
A -->|false| C["ветка else { ... }"]
B --> D["значение = последнее выражение ветки then"]
C --> E["значение = последнее выражение ветки else"]
D --> F["итог if-выражения"]
E --> F
2. else if и линейный стиль кода
Иногда у нас не «два варианта», а «три-четыре». И хорошая новость: цепочка if / else if / else тоже может быть выражением. То есть мы можем присвоить результат сразу, без промежуточных var.
Пример: у нас есть число x, и мы хотим получить строку "positive", "negative" или "zero".
fun main() {
val x = 0
val sign = if (x > 0) {
"positive"
} else if (x < 0) {
"negative"
} else {
"zero"
}
println(sign) // zero
}
Этот стиль особенно хорош тем, что у переменной sign сразу понятный смысл: «это метка знака числа».
Как if-выражение помогает делать код «линейным»
До if-выражений часто пишут так: «объявил переменную var, потом в разных ветках присваиваю». Это рабочий подход, но он постепенно приводит к тому, что переменная живёт слишком долго и меняется слишком много раз.
Сравним два подхода на жизненной задаче: если строка пустая — заменить её на "EMPTY", иначе оставить как есть (после trim()).
Подход с var:
fun main() {
val raw = " Kotlin "
val trimmed = raw.trim()
var label = "EMPTY"
if (trimmed.length != 0) {
label = trimmed
}
println(label) // Kotlin
}
Подход с if-выражением:
fun main() {
val raw = " Kotlin "
val trimmed = raw.trim()
val label = if (trimmed.length == 0) "EMPTY" else trimmed
println(label) // Kotlin
}
Второй вариант читается быстрее: «label — это либо "EMPTY", либо trimmed». И главное: label теперь val, а значит «табличку на двери» нельзя случайно поменять посреди программы.
3. Практический пример: «Касса со скидкой»
Сейчас мы соберём небольшую консольную программу, которая выглядит как реальный сценарий: пользователь вводит сумму заказа, а мы рассчитываем скидку и печатаем итог. Это честный тренажёр, на котором видно, где if как выражение делает код проще.
Идея такая:
1) читаем сумму (как строку), чистим пробелы, пытаемся распарсить в число через toIntOrNull();
2) если не число — печатаем сообщение об ошибке;
3) если число — определяем процент скидки: если сумма >= 1000, то скидка 10%, иначе 0%;
4) формируем сообщение и печатаем.
Шаг 1. Выбираем процент скидки как значение
Начнём с самого важного для этой лекции — присваивания результата ветвления:
fun main() {
val sum = 1200
val discountPercent = if (sum >= 1000) 10 else 0
println("discountPercent = $discountPercent") // discountPercent = 10
}
Здесь видно главное: discountPercent — это результат выбора, значит if-выражение идеально подходит.
Шаг 2. Сообщение тоже можно «вычислить»
Новички часто делают так: в каждой ветке if печатают почти одинаковый текст. Это работает, но быстро приводит к дублированию. Вместо этого мы можем вычислить строку message, а печать оставить одной строкой.
fun main() {
val finalSum = 950
val discountPercent = 0
val message = if (discountPercent > 0) {
"Скидка $discountPercent% применена. Итог: $finalSum"
} else {
"Скидки нет. Итог: $finalSum"
}
println(message) // Скидки нет. Итог: 950
}
Обратите внимание: мы используем блоки { ... }, но последняя строка — именно строка, а не println.
Шаг 3. Полная версия main
Ниже — цельный пример. Он чуть длиннее отдельных кусочков (потому что реальная программа всё-таки делает ввод/проверки), но все приёмы здесь уже знакомы.
fun main() {
print("Введите сумму заказа: ")
val sumText = readln().trim()
val sum = sumText.toIntOrNull()
if (sum == null) {
println("Ошибка: нужно ввести целое число.") // Ошибка: нужно ввести целое число.
} else {
val discountPercent = if (sum >= 1000) 10 else 0
val discount = sum * discountPercent / 100
val finalSum = sum - discount
val message = if (discountPercent > 0) {
"Скидка $discountPercent% (-$discount). Итог: $finalSum"
} else {
"Скидки нет. Итог: $finalSum"
}
println(message) // пример: Скидка 10% (-120). Итог: 1080
}
}
Здесь есть важный стиль мышления: мы стараемся делать переменные «одноразовыми фактами». discountPercent, discount, finalSum, message — это не «ящики, которые меняются», а «результаты вычислений». Когда вы привыкаете к этому, код становится предсказуемее: вы читаете его сверху вниз, как рецепт.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ