7.9 KiB
title, description, tags
| title | description | tags | ||
|---|---|---|---|---|
| Операторы и выражения | Арифметика, равенство == и ===, побитовые операции словами, приоритет операторов |
|
В Kotlin почти все операторы - это вызовы функций. a + b компилируется в a.plus(b), поэтому операторы можно определять для своих типов (Операторные функции).
Арифметические
| Оператор | Функция | Пример |
|---|---|---|
+ |
plus |
2 + 3 → 5 |
- |
minus |
5 - 3 → 2 |
* |
times |
2 * 3 → 6 |
/ |
div |
7 / 2 → 3 |
% |
rem |
7 % 2 → 1 |
Целочисленное деление отбрасывает дробную часть:
println(7 / 2) // 3
println(7.0 / 2) // 3.5
println(7 / 2.0) // 3.5
println(-7 / 2) // -3 — округление к нулю
println(-7 % 2) // -1 — знак остатка совпадает со знаком делимого
Инкремент и декремент
var i = 0
println(i++) // 0, потом i = 1
println(++i) // 2
Работают только с var. Присваивания-операторы (+=, -=, *=, /=, %=) - тоже.
Сравнение
<, >, <=, >= транслируются в compareTo:
println(2 < 3) // true
println("abc" < "abd") // true — лексикографически
Работает для любого типа, реализующего Comparable.
Равенство: == и ===
Это главное место, где Kotlin расходится с Java.
| Оператор | Что делает |
|---|---|
== |
структурное равенство, вызывает equals() |
=== |
ссылочное равенство, сравнивает адреса |
!=, !== |
их отрицания |
val a = "Kotlin"
val b = StringBuilder("Kotlin").toString()
println(a == b) // true — содержимое совпадает
println(a === b) // false — разные объекты
== безопасен для null: a == b разворачивается в a?.equals(b) ?: (b === null).
val s: String? = null
println(s == "text") // false, без NPE
println(s == null) // true
Логические
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 нет - только инфиксные функции:
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.
Операторы диапазонов и коллекций
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 для коллекций
Подробнее: Циклы и диапазоны
Приоритет
От высшего к низшему:
| Уровень | Операторы |
|---|---|
| постфиксные | ++, --, ., ?., !! |
| префиксные | -, +, ++, --, ! |
| приведение | as, as? |
| умножение | *, /, % |
| сложение | +, - |
| диапазон | .., ..< |
| инфиксные функции | and, shl, своя функция infix |
| проверка принадлежности | in, !in |
| проверка типа | is, !is |
| сравнение | <, >, <=, >= |
| равенство | ==, !=, ===, !== |
| конъюнкция | && |
| дизъюнкция | || |
| elvis | ?: |
| присваивание | =, +=, … |
На практике: сомневаетесь - ставьте скобки.
Всё является выражением
if, when, try возвращают значение:
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 }
Подробнее: Условные конструкции
Циклы (for, while) - не выражения, они возвращают Unit.
[!info] Отличия от Java
Java Kotlin ==для объектов - ссылки==- вызовequals().equals()- содержимое===- ссылкиa & bпобитовое Иa and ba >>> ba ushr b~aa.inv()тернарник c ? a : bif (c) a else bоператоры нельзя перегружать можно, через operator funs.equals(t)падает, еслиsnulls == tбезопасенГлавная ловушка при переходе: в Java
==на строках сравнивает ссылки и работает «случайно» благодаря интернированию. В Kotlin==всегда сравнивает содержимое - это и нужно в 99% случаев.Сравнить: Операторы в Java
[!warning] Подводные камни
andиorне ленивые. ДляBooleanэто рабочий код, но правая часть вычислится всегда:fun risky(): Boolean { println("вызвана"); return true } val a = false && risky() // risky() не вызовется val b = false and risky() // "вызвана" напечатаетсяДеление на целочисленный ноль - исключение, на вещественный - нет:
// println(1 / 0) // ArithmeticException println(1.0 / 0) // Infinity println(0.0 / 0) // NaN println(Double.NaN == Double.NaN) // false! NaN не равен себеПриоритет инфиксных функций ниже арифметики:
println(1 shl 2 + 3) // 32, а не 7: это 1 shl (2+3) println((1 shl 2) + 3) // 7
++на упакованном значении создаёт новый объект - в горячем цикле сInt?это аллокации. Используйте не-nullable типы.
Далее: Условные конструкции