Added kotlin guide. (WIP) (15.10.26)

This commit is contained in:
2026-10-15 00:24:21 +03:00
parent dc82207723
commit da3ad9dd1a
13 changed files with 2120 additions and 0 deletions
@@ -0,0 +1,175 @@
---
title: Условные конструкции
description: if и when как выражения, исчерпывающий when, отсутствие тернарного оператора и switch
tags:
- kotlin
- syntax
---
В Kotlin две конструкции ветвления: `if` и `when`. Обе могут работать и как инструкция, и как **выражение**, возвращающее значение.
## if как инструкция
```kotlin
if (x > 0) {
println("положительное")
} else if (x < 0) {
println("отрицательное")
} else {
println("ноль")
}
```
Условие обязано быть `Boolean`. Число или null туда подставить нельзя - компилятор не пропустит.
## if как выражение
```kotlin
val max = if (a > b) a else b
val label = if (x > 0) {
log("positive")
"плюс" // последнее выражение блока — результат
} else {
"минус"
}
```
Правила:
- в режиме выражения ветка `else` **обязательна** - иначе значение не определено
- результат блока - его последнее выражение
- тип результата - общий супертип всех ветвей
```kotlin
val r = if (flag) 1 else "text" // тип Any — общий супертип
```
Тернарного оператора `?:` в смысле Java в Kotlin нет: его роль играет `if`. Оператор `?:` в Kotlin - это **elvis**, у него другая задача ([Операторы null-безопасности](../Null-безопасность/Операторы%20null-безопасности.md)).
## when - замена switch
Две формы. С аргументом:
```kotlin
when (code) {
200 -> println("ok")
404 -> println("not found")
500, 502, 503 -> println("ошибка сервера") // несколько значений в одной ветке
else -> println("неизвестно")
}
```
Без аргумента - вместо цепочки `if/else if`:
```kotlin
when {
x < 0 -> println("отрицательное")
x == 0 -> println("ноль")
else -> println("положительное")
}
```
Ветки проверяются **сверху вниз**, выполняется первая подошедшая. Никакого `break` не нужно - провала в следующую ветку (fall-through) в Kotlin не бывает.
## when как выражение
```kotlin
val name = when (code) {
200 -> "ok"
404 -> "not found"
else -> "unknown"
}
```
В режиме выражения `when` обязан быть **исчерпывающим**: либо есть `else`, либо компилятор видит, что перечислены все варианты.
## Условия в ветках when
Не только константы:
```kotlin
when (x) {
in 1..9 -> "одна цифра" // диапазон
in listOf(10, 20, 30) -> "круглое" // коллекция
!in 0..100 -> "вне диапазона"
else -> "прочее"
}
when (value) {
is String -> value.length // проверка типа + умное приведение
is Int -> value + 1
is List<*> -> value.size
else -> 0
}
when (x) {
computeLimit() -> "предел" // произвольное выражение
else -> "нет"
}
```
После `is` переменная автоматически приводится к проверенному типу - см. [Умные приведения](../Null-безопасность/Умные%20приведения.md).
## Исчерпывающий when без else
С `enum` и `sealed` компилятор сам проверяет полноту:
```kotlin
enum class State { LOADING, READY, ERROR }
fun describe(s: State) = when (s) {
State.LOADING -> "загрузка"
State.READY -> "готово"
State.ERROR -> "ошибка"
} // else не нужен — все варианты перечислены
```
Добавили `State.EMPTY` - код **перестанет компилироваться** во всех таких `when`. Это главная причина предпочитать `sealed` и `enum` строкам и числам: компилятор показывает каждое место, которое нужно обновить.
Подробнее: [Enum class](../Базовые%20типы%20ООП/Enum%20class.md), [Sealed class и interface](../Базовые%20типы%20ООП/Sealed%20class%20и%20interface.md)
## Переменная в when
Аргумент можно объявить прямо в заголовке - он будет виден только внутри:
```kotlin
when (val response = request()) {
is Success -> response.data
is Failure -> response.error
}
```
Удобно, когда `request()` дорогой: вызов происходит один раз.
> [!info] Отличия от Java
> | Java | Kotlin |
> |---|---|
> | `switch` только по числам, строкам, enum | `when` по любым значениям и условиям |
> | нужен `break`, иначе fall-through | провала нет, `break` не нужен |
> | `c ? a : b` | `if (c) a else b` |
> | `if` - только инструкция | `if` - ещё и выражение |
> | `switch` не проверяет полноту (до pattern matching в Java 21) | `when` по enum/sealed проверяется компилятором |
>
> `when` ближе всего к `switch`-выражению из Java 14+ и pattern matching из Java 21, но появился в Kotlin задолго до них и покрывает больше случаев.
>
> Сравнить: [Условные конструкции в Java](../../../Программирование%20на%20Java/Синтаксис%20и%20типы/Базовый%20синтаксис/Условные%20конструкции%20и%20операторы.md)
> [!warning] Подводные камни
> **Полнота проверяется и у `when`-инструкции - но только для `enum` и `sealed`.** С Kotlin 2.0 пропуск ветки - ошибка компиляции даже когда результат не используется:
> ```kotlin
> fun handle(s: State) {
> when (s) {
> State.LOADING -> show()
> State.READY -> done()
> } // error: 'when' expression must be exhaustive.
> } // Add the 'ERROR' branch or an 'else' branch.
> ```
> А вот для `Int`, `String` и прочих открытых типов полноту проверить невозможно - там `when`-инструкция без `else` компилируется молча и просто ничего не делает:
> ```kotlin
> fun handle(code: Int) {
> when (code) {
> 200 -> show()
> 404 -> fail()
> } // для code = 500 тихо ничего не произойдёт
> }
> ```
> Это ещё одна причина моделировать состояния через `enum` и `sealed`, а не через числа и строки: только тогда компилятор на вашей стороне.
>
> **Порядок ветвей важен.** Первая подошедшая выигрывает, более общая ветка выше «съест» частные:
> ```kotlin
> when (x) {
> in 0..100 -> "диапазон" // сработает и для 42
> 42 -> "ответ" // недостижимо
> }
> ```
>
> **`if` без `else` не выражение:**
> ```kotlin
> // val x = if (flag) 1 // ошибка компиляции
> val x = if (flag) 1 else 0 // ok
> ```
>
> **`is List<*>` не проверяет тип элементов** - дженерики стираются в рантайме. `is List<String>` написать нельзя. Подробнее: [Reified и inline](../../Дженерики/Reified%20и%20inline.md)
Далее: [Циклы и диапазоны](Циклы%20и%20диапазоны.md)