Kotlin运算符重载及其他约定摘要

2018-11-09  本文已影响0人  蒋扬海

重载算数运算符

data class Point(val x: Int, val y: Int) {
    operator fun plus(other: Point): Point { //定义一个名为plus的方法
         return Point(x + other.x, y + other.y)  //坐标分别相加,然后返回一个新的点
  }
}
>>> val p1 = Point(10, 20)
>>> val p2 = Point(30, 40)
>>> println(p1 + p2) //通过使用+号来调用plus方法
Point(x=40, y=60)

Kotlin 限定了你能重载哪些运算符,以及你需要在你的类里面定义的对应名字的函数,你不能定义自己的运算符。

可重载的二元算术运算符

表达式 函数名
a*b times
a/b div
a%b mod
a+b plus
a-b minus

从 Java 调用 Kotlin 运算符非常容易:因为每个重载的运算符都被定义为一个函数,你可以像普通函数那样调用它们。 当从 Kotlin 调用 Java 的时候,对于与 Kotlin 约定匹配的函数都可以使用运算符语法来调用。 由于 Java 没有定义任何用于标记运算符函数的语法,所以使用operator修饰符的要求对它不适用,唯一的约束是,参数需要匹配名称和数量。

注意, Kotlin 运算符不会自动支持交换性(交换运算符的左右两边)。 如果希望用户能够使用1.5 * p以外,还能使用p * 1.5,你需要为它定义一个单独的运算符:operator fun Double.times(p: Point): Point

没有用于位运算的特殊运算符

Kotlin 没有为标准数字类型定义任何按位运算符,它使用中缀调用语法的常规函数。

以下是 Kotlin 提供的,用于执行位运算的完整函数列表:

下面的示例展示了这些函数的使用方法:

>>> println(0x0F and 0xF0)
0
>>> println(0x0F or 0xF0)
255
>>> println(0x1 shl 4)
16

集合运算符

Kotlin 标准库为可变集合定义了plusAssign函数:

operator fun <T> MutableCollection<T>.plusAssign(element: T) {
    this.add(element)
}

用法

>>> val numbers = ArrayList<Int>()
>>> numbers += 42
>>> println(numbers[0])
42

当你在代码里面用到+=的时候,理论上plusplusAssign都会被调用到。如果在这种情况下,两个函数都有定义且适用,编译器会报一个错误。

注意事项:

Kotlin 标准库支持集合的两种方法。+-运算符总是返回一个新的集合。+=-=运算符可以用于可变集合,始终在一个地方修改他们;而它们用于只读集合时,会返回一个修改过的副本。(这意味着只有当引用只读集合的变量被声明为var的时候,才能使用+=-=。)

重载一元运算符

可重载的一元的算法运算符

表达式 函数名
+a unaryPlus
-a unaryMinus
!a not
++a, a++ inc
--a, a-- dec

重载比较运算符

在 Kotlin 里面,你可以对任何对象使用比较运算符(==!=><等等),而不仅仅限于基本数据类型。 不像Java一样需要调用equalscompareTo函数,你可以直接使用比较运算符。

注意,===(恒等)运算符不能被重载,恒等运算符与Java中的==运算符是完全相同的。

“get”和“set”
data class MutablePoint(var x: Int, var y: Int)
operator fun MutablePoint.set(index: Int, value: Int) {
//定义一个运算符函数名为set
    when(index) {
        0 -> x = value //根据给出的index修改对应的坐标
        1 -> y = value
        else ->
             throw IndexOutOfBoundsException("Invalid coordinate $index")
   }
}

>>> val p = MutablePoint(10, 20)
>>> p[1] = 42
>>> println(p)
MutablePoint(x=10, y=42)
operator fun Point.get(index: Int): Int {
    return when(index) { //定义一个运算符函数名为get
        0 -> x //根据给出的index返回对应的坐标
        1 -> y
        else ->
              throw IndexOutOfBoundsException("Invalid coordinate $index")
      }
}

>>> val p = Point(10, 20)
>>> println(p[1])
20
“in”的约定

相应的函数叫做contains

operator fun Rectangle.contains(p: Point): Boolean {
    return p.x in upperLeft.x until lowerRight.x &&
           p.y in upperLeft.y until lowerRight.y
} //构建一个区间,检查坐标x是否属于这个区间
//使用until函数来构建一个开区间
>>> val rect = Rectangle(Point(10, 20), Point(50, 50))
>>> println(Point(20, 30) in rect)
true
>>> println(Point(5, 5) in rect)
false
rangTo的约定

..运算符是调用rangeTo函数的一个简洁方法。

operator fun <T: Comparable<T>> T.rangeTo(that: T): ClosedRange<T>

解构声明

一个解构声明看起来像一个普通的变量声明,但它在括号中有多个变量。

>>> val p = Point(10, 20)
>>> val (x, y) = p //声明变量x,y,然后用p的组件来初始化
>>> println(x)
10
>>> println(y)
20

要在解构声明中初始化每个变量,将调用名为componentN的函数,其中N是声明中变量的位置。

解构声明不仅可以用作函数中的顶层语句,还可以用在其他可以声明变量的地方,例如in循环。

fun printEntries(map: Map<String, String>) {
    for ((key, value) in map) { //在in循环中用解构声明
        println("$key -> $value")
    }
}

Kotlin 标准库给map增加了一个扩展的iterator函数,用来返回map条目的迭代器。因此,与Java不同的是,你可以直接迭代map。

for (entry in map.entries) {
    val key = entry.component1()
    val value = entry.component2()
    // ...
}

委托属性

基本语法

class Foo {
    var p: Type by Delegate()
}

惰性初始化和"by lazy()"

惰性初始化 是一种常见的模式,直到在第一次访问时该属性的时候,才根据需要创建一部分对象

class Person(val name: String) {
    val emails by lazy { loadEmails(this) }
}

默认情况下,lazy函数是线程安全的,如果需要,你可以设置其他选项来告诉它要使用哪个锁,或者完全避开同步,如果该类永远不会在多线程环境中使用。

注意,要实现自己的可以用作委托的类,或者说实现自定义的委托属性,请参考《Kotlin 实战》一书 7.5.3节。理解代理属性的原理,并在实际编程中使用,会增加代码的简介度。

上一篇下一篇

猜你喜欢

热点阅读