Files
kotlin-fork/spec-docs/operator-conventions.md
T
2015-02-27 19:32:56 +03:00

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 be T.
  • Looks up a function plus() with no parameters for the receiver T, 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 +a has type R.
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 be T.
  • Looks up a function inc() with no parameters, applicable to the receiver of type T.
  • If the function returns a type R, then it must be a subtype of T.

The effect of computing the expression is:

  • Store the initial value of a to a temporary storage a0,
  • Assign the result of a.inc() to a,
  • Return a0 as 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() to a,
  • Return the new value of a as 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, and null == null is true.
  • 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() for plusAssign()) is available too, report error (ambiguity).
    • Make sure its return type is Unit, and report an error otherwise.
    • Generate code for a.plusAssign(b)
  • Otherwise, try to generate code for a = a + b (this includes a type check: the type of a + b must be a subtype of a).

Note: assignments are NOT expressions in Kotlin.