183 lines
7.9 KiB
Markdown
183 lines
7.9 KiB
Markdown
---
|
||
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)
|