如你所知,java在标准库中有一些与特定的类相关联的语言特性。例如,实现java.lang.Iterable接口的对象可以在for循环中使用;实现了java.lang.AutoCloseable接口的对象可以在try-with-resource语句中使用。
一、重载算术运算符
1、重载二元运算符
注意,如何使用operator关键字来声明plus函数。用于重载运算符的所有函数都需要用该关键字来标记,用来表示你打算把这个函数作为相应的约定的实现,并且不是碰巧地定义一个同名函数。
在使用了operator修饰符声明了plus函数之后,你就可以直接使用+号来求和了。事实上,这里它调用的是plus函数。
除了把这个运算符声明为一个成员函数外,也可以把它定义为一个扩展函数。
和其他一些语言相比,在kotlin中不管是定义还是使用重载运算符都更为简单,因为你不能定义自己的运算符。kotlin限定了你能重载哪些运算符,以及您需要在你的类中定义的对应名字的函数。
自定义类型的运算符,基本上和标准数字类型的运算符有着相同的优先级。例如,如果是a + b * c,乘法将始终在添加之前执行,即使你已经自己定义了这些运算符。
当你在定义一个运算符的时候,不要求两个运算数是相同的类型。例如,让我们定义一个运算符,它允许你用一个数字来缩放一个点,可以用它在不同坐标系之间做转换。
注意,kotlin中运算符不会自动支持交换性(交换运算符的左右两边)。如果希望用户能够使用1.5 * p以外,还能使用p * 1.5,你需要为它定义一个单独的运算符:operator fun Double.times(p: Point): Point。
运算符函数的返回类型也可以不同于任一运算数的类型。 注意,和普通的函数一样,可以重载operator函数:可以定义多个同名的,但是参数类型不同的方法。
2、重载复合赋值运算符
通常情况下,当你在定义像plus这样的运算符函数时,kotlin不止支持+号运算,也支持+=。像+=、-=等这些运算符被称为复合赋值运算符。
在一些情况下,定义+=运算可以修改使用它的变量所引用的对象,但不会重新分配引用。将一个元素添加到可变集合,就是一个很好的例子:
如果你定义了一个返回值为Unit,名为plusAssign的函数,kotlin将会在用到+=运算符的地方调用它。其他二元算术符也有命名相似的对应函数:如minusAssign、TimesAssign等。
kotlin标准库为可变集合定义了plusAssign函数。
当你在代码中用到了+=的时候,理论上plus和plusAssign都可能被调用。如果在这种情况下,两个函数都有定义且适用,编译器会报错。一种可行的解决办法是:替换运算符的使用为普通函数调用。另一个办法是,使用val替代var,这样plusAssign运算符就不再适用。但一般来说,最好一致地设计出新的类:尽量不要同时给一个类添加plus和plusAssign运算。如果像前面的一个示例中的Point,这个类是不可变的,那么就应该只提供返回一个新值(如plus)的运算。如果一个类是可变的,比如构建器,那么只需要提供plusAssign和类似的运算就够了。
kotlin标准库支持集合的这两种方法。+和-运算符总是返回一个新的集合。+=和-=运算符用于可变集合时,始终在一个地方修改它们:而它们用于只读集合时,会返回一个修改过的副本(这意味着只有当引用只读集合的变量被声明为var的时候,才能使用+=和-=)。作为它们的运算符,可以使用单个元素,也可以使用元素类型一致的其他集合。
最后来看看集合的二元运算符是如何实现的。
/**
* Returns a list containing all elements of the original collection and then the given [element].
*/
public operator fun <T> Collection<T>.plus(element: T): List<T> {
val result = ArrayList<T>(size + 1)
result.addAll(this)
result.add(element)
return result
}
/**
* Adds the specified [element] to this mutable collection.
*/
@kotlin.internal.InlineOnly
public inline operator fun <T> MutableCollection<in T>.plusAssign(element: T) {
this.add(element)
}