type: doc layout: reference title: "Operator overloading"

category: "Syntax"

运算符重载

Kotlin允许我们实现一些我们自定义类型的运算符实现。这些运算符有固定的表示(像 + 或者 *) ,和固定的优先级。为实现这样的运算符,我们提供了固定名字的成员函数扩展函数,比如二元运算符的左值和一元运算符的参数类型。

转换

这里我们描述了一些常用运算符的重载。

一元运算符

表达式 翻译为
+a a.plus()
-a a.minus()
!a a.not()

这张表解释了当编译器运行时,比如,表达式 +a ,是这样运行的:

  • 决定a的类型, 假设为T
  • 寻找接收者是T的无参函数plus(),例如一个成员方法或者扩展方法。
  • 如果找不到或者不明确就返回一个错误。
  • 如果函数是当前函数或返回类型是R则表达式+aR类型。

注意 这些操作符和其它的一样, 都被优化为基本类型并且不会产生多余的开销。

表达式 翻译为
a++ a.inc() + 见下方
a-- a.dec() + 见下方

这些操作符允许修改接收者和返回类型。

inc()/dec() 不应该改变接收对象.
"修改接受者"你应该修改接收者变量而非对象。 {:.note}

编译器是这样解决有后缀的操作符的比如a++:

  • 决定a的类型, 假设为T
  • 查找接收类型为T无参数函数`inc()。
  • 如果返回类型为R,那么RT子类型.

计算表达式的步骤是:

  • a的值存在a0中,
  • a.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.mod(b)
a..b a.rangeTo(b)

编译器只是解决了该表中翻译为列的表达式。

Expression Translated to
a in b b.contains(a)
a !in b !b.contains(a)

in 和 !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(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)

{:#assignments}

在分配 a+= b时编译器是下面这样实现的:

  • 右边函数是否可用。
    • 对应的二元函数是否 (如plus()plusAssign())也可用, 不可用就报告错误.
    • 确定它的返回值是Unit类型, 否则报告错误。
    • 生成a.plusAssign(b) *否则试着生成a = a + b代码 (这里包含类型检查: a + b一定要是a的子类型).

注意: assignments在Kotlin中不是表达式. {:#Equals}

表达式 翻译为
a == b a?.equals(b) ?: b.identityEquals(null)
a != b !(a?.equals(b) ?: b.identityEquals(null))

注意: ===!== (实例检查)不能重载, 所以没有转换方式。

==运算符有两点不同:

  • 它被翻译成一个复杂的表达式,用于筛选空值,而且 null == null 是真.
  • 它需要带有特定签名的函数,而不仅仅是特定名称的函数,像下面这样:
fun equals(other: Any?): Boolean

或者用相同的参数列表和返回类型的扩展函数.

标志 翻译为
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

命名函数的中缀调用

我们可以通过中缀函数的调用. 来模拟自定义中缀操作符。