Files
obsidian/Kotlin/Синтаксис и типы/Базовый синтаксис/Операторы и выражения.md
T

183 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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)