Перегрузка операторов
Kotlin позволяет нам предоставлять реализации для предопределённого набора операторов для наших типов. Эти операторы имеют фиксированное символьное представление (например, + или *) и фиксированный приоритет. Для реализации оператора мы предоставляем член-функцию или расширяющую функцию с фиксированным именем для соответствующего типа, т.е. типа левой части для бинарных операций и типа аргумента для унарных. Функции, перегружающие операторы, должны быть помечены модификатором operator.
Далее мы описываем соглашения, регулирующие перегрузку операторов для различных операторов.
Унарные операции
Унарные префиксные операторы
| Выражение | Переводится в |
|---|---|
+a | a.unaryPlus() |
-a | a.unaryMinus() |
!a | a.not() |
Эта таблица показывает, что когда компилятор обрабатывает, например, выражение +a, он выполняет следующие шаги:
- Определяет тип
a, пусть это будетT; - Ищет функцию
unaryPlus()с модификаторомoperatorи без параметров для получателяT, т.е. член-функцию или расширяющую функцию; - Если функция отсутствует или неоднозначна, это ошибка компиляции;
- Если функция присутствует и её тип возвращаемого значения
R, то выражение+aимеет типR;
Примечание, что эти операции, как и все остальные, оптимизированы для базовых типов и не вводят накладные расходы вызовов функций для них.
В качестве примера, вот как можно перегрузить унарный оператор минус:
data class Point(val x: Int, val y: Int)
operator fun Point.unaryMinus() = Point(-x, -y)
val point = Point(10, 20)
fun main() {
println(-point) // prints "Point(x=-10, y=-20)"
}
Инкременты и декременты
| Выражение | Переводится в |
|---|---|
a++ | a.inc() + см. ниже |
a-- | a.dec() + см. ниже |
Функции inc() и dec() должны возвращать значение, которое будет присвоено переменной, к которой применялась операция ++ или --. Они не должны изменять объект, к которому применялась операция inc или dec.
Компилятор выполняет следующие шаги для разрешения оператора в постфиксной форме, например, a++.
- Определяет тип
a, пусть это будетT; - Ищет функцию
inc()с модификаторомoperatorи без параметров, применимую к получателю типаT; - Проверяет, что тип возвращаемого значения функции является подтипом
T.
Эффект вычисления выражения:
- Сохранить начальное значение
aв временное хранилищеa0; - Присвоить результат
a0.inc()переменнойa; - Вернуть
a0в качестве результата выражения.
Для a-- шаги полностью аналогичны.
Для префиксных форм ++a и --a разрешение работает аналогично, а эффект:
- Присвоить результат
a.inc()переменнойa; - Вернуть новое значение
aв качестве результата выражения.
Бинарные операции
Арифметические операторы
| Выражение | Переводится в |
|---|---|
a + b | a.plus(b) |
a - b | a.minus(b) |
a * b | a.times(b) |
a / b | a.div(b) |
a % b | a.rem(b), a.mod(b) (устарело) |
a..b | a.rangeTo(b) |
Для операций в этой таблице компилятор просто разрешает выражение в столбце «Переводится в».
Обратите внимание, что оператор rem поддерживается начиная с Kotlin 1.1. Kotlin 1.0 использует оператор mod, который устарел в Kotlin 1.1.
Пример
Ниже представлен пример класса Counter, который начинается с заданного значения и может быть инкрементирован с помощью перегруженного оператора +:
data class Counter(val dayIndex: Int) {
operator fun plus(increment: Int): Counter {
return Counter(dayIndex + increment)
}
}
'In' оператор
| Выражение | Переводится в |
|---|---|
a in b | b.contains(a) |
a !in b | !b.contains(a) |
Для in и !in процедура аналогична, но порядок аргументов изменён.
Оператор индексирования
| Выражение | Переводится в |
|---|---|
a[i] | a.get(i) |
a[i, j] | a.get(i, j) |
a[i_1, ..., i_n] | a.get(i_1, ..., i_n) |
a[i] = b | a.set(i, b) |
a[i, j] = b | a.set(i, j, b) |
a[i_1, ..., i_n] = b | a.set(i_1, ..., i_n, b) |
Квадратные скобки переводятся в вызовы get и set с соответствующим числом аргументов.
Оператор вызова
| Выражение | Переводится в |
|---|---|
a() | a.invoke() |
a(i) | a.invoke(i) |
a(i, j) | a.invoke(i, j) |
a(i_1, ..., i_n) | a.invoke(i_1, ..., i_n) |
Скобки переводятся в вызовы invoke с соответствующим числом аргументов.
Расширенные присваивания
| Выражение | Переводится в |
|---|---|
a += b | a.plusAssign(b) |
a -= b | a.minusAssign(b) |
a *= b | a.timesAssign(b) |
a /= b | a.divAssign(b) |
a %= b | a.remAssign(b), a.modAssign(b) (устарело) |
Для операций присваивания, например, a += b, компилятор выполняет следующие шаги:
- Если функция из правого столбца доступна
- Если соответствующая двоичная функция (например,
plus()дляplusAssign()) также доступна, сообщить об ошибке (неопределённость), - Убедиться, что её тип возвращаемого значения
Unit, и в противном случае сообщить об ошибке, - Сгенерировать код для
a.plusAssign(b);
- Если соответствующая двоичная функция (например,
- В противном случае, попробовать сгенерировать код для
a = a + b(включая проверку типов: типa + bдолжен быть подтипомa).
Примечание: присваивания — НЕ выражения в Kotlin.
Операторы равенства и неравенства
| Выражение | Переведено в |
|---|---|
a == b | a?.equals(b) ?: (b === null) |
a != b | !(a?.equals(b) ?: (b === null)) |
Эти операторы работают только с функцией equals(other: Any?): Boolean, которую можно переопределить для реализации пользовательской проверки равенства. Любая другая функция с таким же именем (например, equals(other: Foo)) не будет вызвана.
Примечание: === и !== (проверки тождества) не перегружаются, поэтому для них нет никаких соглашений.
Операция == является специальной: она переводится в сложное выражение, которое проверяет на наличие null. null == null всегда истинно, а x == null для непустого x всегда ложно и не вызовет x.equals().
Операторы сравнения
| Выражение | Переведено в |
|---|---|
a > b | a.compareTo(b) > 0 |
a < b | a.compareTo(b) < 0 |
a >= b | a.compareTo(b) >= 0 |
a <= b | a.compareTo(b) <= 0 |
Все сравнения переводятся в вызовы compareTo, которые должны возвращать Int.
Операторы делегирования свойств
Функции-операторы provideDelegate, getValue и setValue описаны в Делегированные свойства.
Вызовы infix для именованных функций
Можно имитировать пользовательские infix-операции, используя вызовы infix-функций.
© 2010–2020 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/reference/operator-overloading.html