5.1 KiB
Operator Conventions
Kotlin allows us to provide implementations for a predefined set of operators on our types. These operators have fixed symbolic representation
(like + or *) and fixed (see grammar). To implement an operator, we provide a member function
or an extension function with a fixed name, for the corresponding type, i.e. left-hand side type for binary operations and argument type for unary ones.
Here we describe the conventions that regulate operator overloading for different operators.
Unary operations
| Expression | Translated to |
|---|---|
+a |
a.plus() |
-a |
a.minus() |
!a |
a.not() |
This table says that when the compiler processes, for example, an expression +a, it performs the following steps:
- Determines the type of
a, let it beT. - Looks up a function
plus()with no parameters for the receiverT, i.e. a member function or an extension function. - If the function is absent or ambiguous, it is a compilation error.
- If the function is present and its return type is
R, the expression+ahas typeR.
| Expression | Translated to |
|---|---|
a++ |
a.inc() + see below |
a-- |
a.dec() + see below |
These operations are supposed to change their receiver and (optionally) return a value.
inc()/dec()shouldn't mutate the receiver object.
By "changing the receiver" we mean the receiver-variable, not the receiver object. {:.note}
The compiler performs the following steps for resolution of an operator in the postfix form, e.g. a++:
- Determines the type of
a, let it beT. - Looks up a function
inc()with no parameters, applicable to the receiver of typeT. - If the function returns a type
R, then it must be a subtype ofT.
The effect of computing the expression is:
- Store the initial value of
ato a temporary storagea0, - Assign the result of
a.inc()toa, - Return
a0as a result of the expression.
For a-- the steps are completely analogous.
For the prefix forms ++a and --a resolution works the same way, and the effect is:
- Assign the result of
a.inc()toa, - Return the new value of
aas a result of the expression.
Binary operations
| Expression | Translated to |
|---|---|
a + b |
a.plus(b) |
a - b |
a.minus(b) |
a * b |
a.times(b) |
a / b |
a.div(b) |
a % b |
a.mod(b) |
a..b |
a.rangeTo(b) |
For the operations in this table, the compiler just resolves the expression in the Translated to column.
| Expression | Translated to |
|---|---|
a in b |
b.contains(a) |
a !in b |
!b.contains(a) |
For in and !in the procedure is the same, but the order of arguments is reversed.
{:#in}
{:#Equals}
| Expression | Translated to |
|---|---|
a == b |
a?.equals(b) ?: b.identityEquals(null) |
a != b |
!(a?.equals(b) ?: b.identityEquals(null)) |
Note: === and !== (identity checks) are not overloadable, so no conventions exist for them
The == operation is special in two ways:
- It is translated to a complex expression that screens for
null's, andnull == nullistrue. - It looks up a function with a specific signature, not just a specific name. The function must be declared as
fun equals(other: Any?): Boolean
Or an extension function with the same parameter list and return type.
| Symbol | Translated to |
|---|---|
a > b |
a.compareTo(b) > 0 |
a < b |
a.compareTo(b) < 0 |
a >= b |
a.compareTo(b) >= 0 |
a <= b |
a.compareTo(b) <= 0 |
All comparisons are translated into calls to compareTo, that is required to return Int.
Indexing and invocations
| Symbol | Translated to |
|---|---|
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) |
Square brackets are translated to calls to get and set with appropriate numbers of arguments.
| Symbol | Translated to |
|---|---|
a(i) |
a.invoke(i) |
a(i, j) |
a.invoke(i, j) |
a(i_1, ..., i_n) |
a.invoke(i_1, ..., i_n) |
Parentheses are translated to calls to invoke with appropriate number of arguments.
Assignments
| Expression | Translated to |
|---|---|
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) |
For the assignment operations, e.g. a += b, the compiler performs the following steps:
- If the function from the right column is available
- If the corresponding binary function (i.e.
plus()forplusAssign()) is available too, report error (ambiguity). - Make sure its return type is
Unit, and report an error otherwise. - Generate code for
a.plusAssign(b)
- If the corresponding binary function (i.e.
- Otherwise, try to generate code for
a = a + b(this includes a type check: the type ofa + bmust be a subtype ofa).
Note: assignments are NOT expressions in Kotlin.