--- title: Операторы и выражения description: Арифметика, равенство == и ===, побитовые операции словами, приоритет операторов tags: - kotlin - syntax --- В Kotlin почти все операторы - это вызовы функций. `a + b` компилируется в `a.plus(b)`, поэтому операторы можно определять для своих типов ([Операторные функции](../../Функциональный%20Kotlin/Инфиксные%20и%20операторные%20функции.md)). ## Арифметические | Оператор | Функция | Пример | |---|---|---| | `+` | `plus` | `2 + 3` → 5 | | `-` | `minus` | `5 - 3` → 2 | | `*` | `times` | `2 * 3` → 6 | | `/` | `div` | `7 / 2` → 3 | | `%` | `rem` | `7 % 2` → 1 | Целочисленное деление отбрасывает дробную часть: ```kotlin println(7 / 2) // 3 println(7.0 / 2) // 3.5 println(7 / 2.0) // 3.5 println(-7 / 2) // -3 — округление к нулю println(-7 % 2) // -1 — знак остатка совпадает со знаком делимого ``` ### Инкремент и декремент ```kotlin var i = 0 println(i++) // 0, потом i = 1 println(++i) // 2 ``` Работают только с `var`. Присваивания-операторы (`+=`, `-=`, `*=`, `/=`, `%=`) - тоже. ## Сравнение `<`, `>`, `<=`, `>=` транслируются в `compareTo`: ```kotlin println(2 < 3) // true println("abc" < "abd") // true — лексикографически ``` Работает для любого типа, реализующего `Comparable`. ## Равенство: == и === Это главное место, где Kotlin расходится с Java. | Оператор | Что делает | |---|---| | `==` | структурное равенство, вызывает `equals()` | | `===` | ссылочное равенство, сравнивает адреса | | `!=`, `!==` | их отрицания | ```kotlin val a = "Kotlin" val b = StringBuilder("Kotlin").toString() println(a == b) // true — содержимое совпадает println(a === b) // false — разные объекты ``` `==` безопасен для null: `a == b` разворачивается в `a?.equals(b) ?: (b === null)`. ```kotlin val s: String? = null println(s == "text") // false, без NPE println(s == null) // true ``` ## Логические ```kotlin val r1 = true && false // ленивый И val r2 = true || false // ленивый ИЛИ val r3 = !true // НЕ val r4 = true and false // НЕ ленивый — оба операнда вычисляются val r5 = true or false // НЕ ленивый val r6 = true xor true // false ``` В обычном коде используют `&&` и `||`: они не вычисляют правую часть, если результат уже известен. ## Побитовые операции Символов `&`, `|`, `^`, `<<`, `>>` для чисел в Kotlin **нет** - только инфиксные функции: ```kotlin val a = 0b1100 val b = 0b1010 println(a and b) // 8 = 0b1000 println(a or b) // 14 = 0b1110 println(a xor b) // 6 = 0b0110 println(a.inv()) // -13 — побитовое НЕ println(1 shl 4) // 16 — сдвиг влево println(16 shr 2) // 4 — сдвиг вправо со знаком println(-16 ushr 2) // беззнаковый сдвиг вправо ``` Доступны для `Int` и `Long`. ## Операторы диапазонов и коллекций ```kotlin println(5 in 1..10) // true println(5 !in 1..10) // false println("a" in listOf("a","b")) // true — вызывает contains() val list = listOf(1, 2) println(list + 3) // [1, 2, 3] — plus для коллекций ``` Подробнее: [Циклы и диапазоны](Циклы%20и%20диапазоны.md) ## Приоритет От высшего к низшему: | Уровень | Операторы | |---|---| | постфиксные | `++`, `--`, `.`, `?.`, `!!` | | префиксные | `-`, `+`, `++`, `--`, `!` | | приведение | `as`, `as?` | | умножение | `*`, `/`, `%` | | сложение | `+`, `-` | | диапазон | `..`, `..<` | | инфиксные функции | `and`, `shl`, своя функция `infix` | | проверка принадлежности | `in`, `!in` | | проверка типа | `is`, `!is` | | сравнение | `<`, `>`, `<=`, `>=` | | равенство | `==`, `!=`, `===`, `!==` | | конъюнкция | `&&` | | дизъюнкция | `\|\|` | | elvis | `?:` | | присваивание | `=`, `+=`, … | На практике: сомневаетесь - ставьте скобки. ## Всё является выражением `if`, `when`, `try` возвращают значение: ```kotlin val max = if (a > b) a else b val name = when (code) { 200 -> "ok" else -> "unknown" } val n = try { text.toInt() } catch (e: NumberFormatException) { 0 } ``` Подробнее: [Условные конструкции](Условные%20конструкции.md) Циклы (`for`, `while`) - **не** выражения, они возвращают `Unit`. > [!info] Отличия от Java > | Java | Kotlin | > |---|---| > | `==` для объектов - ссылки | `==` - вызов `equals()` | > | `.equals()` - содержимое | `===` - ссылки | > | `a & b` побитовое И | `a and b` | > | `a >>> b` | `a ushr b` | > | `~a` | `a.inv()` | > | тернарник `c ? a : b` | `if (c) a else b` | > | операторы нельзя перегружать | можно, через `operator fun` | > | `s.equals(t)` падает, если `s` null | `s == t` безопасен | > > Главная ловушка при переходе: в Java `==` на строках сравнивает ссылки и работает «случайно» благодаря [интернированию](../../../Программирование%20на%20Java/Синтаксис%20и%20типы/Работа%20с%20данными%20и%20типами/Строки.md). В Kotlin `==` всегда сравнивает содержимое - это и нужно в 99% случаев. > > Сравнить: [Операторы в Java](../../../Программирование%20на%20Java/Синтаксис%20и%20типы/Базовый%20синтаксис/Операторы%20и%20выражения.md) > [!warning] Подводные камни > **`and` и `or` не ленивые.** Для `Boolean` это рабочий код, но правая часть вычислится всегда: > ```kotlin > fun risky(): Boolean { println("вызвана"); return true } > val a = false && risky() // risky() не вызовется > val b = false and risky() // "вызвана" напечатается > ``` > > **Деление на целочисленный ноль - исключение, на вещественный - нет:** > ```kotlin > // println(1 / 0) // ArithmeticException > println(1.0 / 0) // Infinity > println(0.0 / 0) // NaN > println(Double.NaN == Double.NaN) // false! NaN не равен себе > ``` > > **Приоритет инфиксных функций ниже арифметики:** > ```kotlin > println(1 shl 2 + 3) // 32, а не 7: это 1 shl (2+3) > println((1 shl 2) + 3) // 7 > ``` > > **`++` на упакованном значении создаёт новый объект** - в горячем цикле с `Int?` это аллокации. Используйте не-nullable типы. Далее: [Условные конструкции](Условные%20конструкции.md)