美文网首页
Kotlin运算符重载及其他约定摘要

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

作者: 蒋扬海 | 来源:发表于2018-11-09 14:59 被阅读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 提供的,用于执行位运算的完整函数列表:

    • shl — 带符号左移
    • shr — 带符号右移
    • ushr — 无符号右移
    • and — 按位与
    • or — 按位或
    • xor — 按位异或
    • inv — 按位取反

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

    >>> 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是声明中变量的位置。

    • 对于数据类,编译器为每个在主构造方法中声明的属性生成一个componentN函数。

    • 手动为非数据类声明这些功能:

      class Point(val x: Int, val y: Int) {
          operator fun component1() = x
          operator fun component2() = y
      }
      
    • 标准库只允许使用此语法来访问一个对象的前五个元素。

    解构声明不仅可以用作函数中的顶层语句,还可以用在其他可以声明变量的地方,例如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节。理解代理属性的原理,并在实际编程中使用,会增加代码的简介度。

    相关文章

      网友评论

          本文标题:Kotlin运算符重载及其他约定摘要

          本文链接:https://www.haomeiwen.com/subject/heloxqtx.html