Перегрузка операторов
Kotlin позволяет реализовывать предопределённый набор операторов для ваших типов. Эти операторы имеют фиксированное
символическое представление (вроде +
или *
) и фиксированные приоритеты. Для реализации оператора предоставьте
функцию-член или функцию-расширение с фиксированным именем и с
соответствующим типом, т. е. левосторонним типом для бинарных операций или типом аргумента для унарных операций.
Функции, которые перегружают операторы, должны быть отмечены модификатором operator
.
interface IndexedContainer {
operator fun get(index: Int)
}
При переопределении перегрузок оператора вы можете опускать operator
.
class OrdersList: IndexedContainer {
override fun get(index: Int) { /*...*/ }
}
Унарные операторы
Унарные префиксные операторы
Выражение | Транслируется в |
---|---|
+a |
a.unaryPlus() |
-a |
a.unaryMinus() |
!a |
a.not() |
Под транслированием, понимается представление вашего кода после компиляции.
Эта таблица демонстрирует, что компилятор при обрабатывании, к примеру, выражения +a
, осуществляет следующие действия:
- Определяет тип выражения
a
, пусть это будетT
; - Ищет функцию
unaryPlus()
с модификаторомoperator
без параметров для приёмника типаТ
, т. е. функцию-член или функцию-расширение; - Если функция отсутствует или неоднозначная, возвращается ошибка компиляции;
- Если функция присутствует и
R
- её возвращаемый тип, выражение+a
имеет ТипR
.
Эти операции, как и все остальные, оптимизированы для основных типов и не требуют дополнительных затрат на вызовы этих функций для них.
Например, вы можете перегрузить оператор унарного минуса:
data class Point(val x: Int, val y: Int)
operator fun Point.unaryMinus() = Point(-x, -y)
fun main() {
val point = Point(10, 20)
println(-point) // выведет "Point(x=-10, y=-20)"
}
Инкремент и декремент
Выражение | Транслируется в |
---|---|
a++ |
a.inc() + см. ниже |
a-- |
a.dec() + см. ниже |
Функции inc()
и dec()
должны возвращать значение, которое будет присвоено переменной, к которой была применена
операция ++
или --
. Они не должны изменять сам объект, для которого были вызваны эти функции.
Компилятор осуществляет следующие шаги, обрабатывая операторы в постфиксной форме, например a++
:
- Определяет тип переменной
a
, пусть это будетT
; - Ищет функцию
inc()
с модификаторомoperator
без параметров, применимую для приёмника типаТ
. - Проверяет, что возвращаемый тип такой функции является подтипом
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..b |
a.rangeTo(b) |
Для перечисленных в таблице операций компилятор всего лишь решает выражение из колонки Транслируется в.
Ниже приведен пример класса 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.modAssign(b) |
Для присваивающих операций, таких как a += b
, компилятор осуществляет следующие шаги:
- Если функция из правой колонки таблицы доступна:
- Если соответствующая бинарная функция (например
plus()
дляplusAssign()
) также доступна,a
- изменяемая переменная и возвращаемый типplus
является подтипом типаa
, то фиксируется ошибка (неоднозначность); - Проверяется, что возвращаемое значение функции
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
для non-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
описаны в Делегированные свойства.
Инфиксные вызовы именованных функций
Вы можете имитировать инфиксные операции, используя инфиксную запись.