美文网首页Kotlin编程Kotlin
Kotlin学习笔记(四)约定(一)

Kotlin学习笔记(四)约定(一)

作者: 风少侠 | 来源:发表于2018-02-27 15:44 被阅读36次

    约定,指使用与常规方法调用语法不同的、更简洁的符号,调用有着特殊命名的函数。举个例子:如果你在类中定义了一个名为plus的特殊方法(需要用operator关键字来声明该方法),那么按照约定,你就可以在该类的实例上使用+运算符。

    算术运算符约定

    二元算术运算符

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

    我们使用运算符时,实际上调用的是与之对应的函数。 注意事项:

    • 这种自定义的运算符,基本与标准数字类型的运算符具有相同的优先级,a+b*c时会先执行乘法(times函数)。
    • 不支持交换性,即a+b和b+a是不同的,如果需要支持交换性,可以定义一个额外的运算符函数。
    fun main(args: Array<String>) {
        val p1 = Point(1, 10)
        val p2 = Point(2, 20)
        println(p1 + p2) //Point(x=3, y=30)
        println(p1 - p2) //Point(x=-1, y=-10)
        println(p1 * p2) //Point(x=2, y=200)
        println(p1 / p2) //Point(x=0, y=0)
        println(p1 % p2) //Point(x=1, y=10)
    }
    
    data class Point(private val x: Int, private val y: Int) {
        operator fun plus(p: Point): Point {
            return Point(x + p.x, y + p.y)
        }
    
        operator fun minus(p: Point): Point {
            return Point(x - p.x, y - p.y)
        }
    
        operator fun times(p: Point): Point {
            return Point(x * p.x, y * p.y)
        }
    
        operator fun div(p: Point): Point {
            return Point(x / p.x, y / p.y)
        }
    
        operator fun rem(p: Point): Point {
            return Point(x % p.x, y % p.y)
        }
    
    }
    

    复合赋值运算符

    表达式 函数名
    a += b plusAssign
    a -= b minusAssign
    a *= b timesAssign
    a /= b divAssign
    a %= b remAssign

    使用方法基本同二元算术运算符,但方法必须返回Unit类型

    需要注意的是:使用+=运算符时,理论上plus和plusAssign函数都有可能被调用,即a+=b可以被转换成a=a.plus(b),也可以被转换成a.plusAssign(b)。如果类中同时定义了plus和plusAssign方法,编译器会报错。解决方法有两种:

    • 不使用运算符,改用普通的函数调用。
    • 使用val对象,这样plusAssign就不再适用。

    一元运算符

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

    使用方法基本同二元算术运算符,注意依然遵守前缀和后缀运算规则,前缀先改变自身再使用,后缀先使用再改变。

    fun main(args: Array<String>) {
        val p1 = Point(1, 10)
        val p2 = Point(2, 20)
    
        println(-p1) //Point(x=-1, y=-10)
    }
    
    data class Point(private var x: Int, private var y: Int) {
        operator fun unaryMinus(): Point {
            return Point(-x, -y)
        }
    }
    

    比较运算符

    等号运算符

    表达式 函数名
    == equals
    != !equals

    ==和!=运算符可以用于可空对象。 a和b相等或者a和b都为null时会返回true。

    a == b 会被转换成 a?.equals(b) ?: ( b==null )

    注意equals不能实现为扩展函数,因为继承自Any类的实现始终优先于扩展函数。

    Kotlin另外提供了恒等运算符(===)来检查两个参数是否为同一对象的引用,效果等同于Java中的==运算符。

    排序运算符

    表达式 函数名
    >,<,>=,<= compareTo

    compareTo的返回类型必须为Int。所有Java中实现了Comparable接口的类,都可以在Kotlin中使用排序运算符语法。

    a >= b 会被转换成 a.compareTo(b) >= 0

    fun main(args: Array<String>) {
        val p1 = Point(1, 10)
        val p2 = Point(2, 20)
        println(p1 > p2) //false
        println(p1 < p2) //true
    }
    
    data class Point(private var x: Int, private var y: Int) {
        operator fun compareTo(p: Point): Int {//也可以实现Comparable接口
            return x - p.x
        }
    }
    

    集合和区间的相关约定

    通过下标访问元素

    表达式 函数名
    x[a] x.get(a)
    x[a,b] x.get(a,b)
    x[a] = value x.set(a,value)

    在Kotlin中,下标运算符是一个约定,使用下标运算符获取元素会被转换成get方法的调用,写入元素则会转换成set方法的调用。向Map和List都已经定义了这些方法,因此我们可以使用a[b]这种形式来访问和修改集合中的某个元素。

    fun main(args: Array<String>) {
        val p = Point(1, 10)
    
        println(p[1]) //10
        p[1] = 20
        println(p) //Point(x=1, y=20)
    }
    
    data class Point(private var x: Int, private var y: Int) {
        operator fun get(index: Int): Int {
            return when (index) {
                0 -> x
                1 -> y
                else -> throw IndexOutOfBoundsException("Error")
            }
        }
    
        //set方法的最后一个参数用来接收赋值语句等号右边的值,其他参数同get方法一样用来表示方括号中的下标
        operator fun set(index: Int, value: Int) {
            when (index) {
                0 -> x = value
                1 -> y = value
                else -> throw IndexOutOfBoundsException("Error")
            }
        }
    }
    

    需要注意的是get方法的参数可以是任何类型,不只是Int,而且参数个数也可以是多个,下标运算符仅仅是get和set方法的一个简写,并不是严格意义上的下标索引。

    rangeTo的约定

    表达式 函数名
    a . . b a.rangeTo(b)

    rangeTo用于创建一个闭区间,你可以为自己的类创建一个rangeTo方法来支持该运算符,或者是实现Comparable接口,因为Kotlin的标准库中已经为Comparable定义了名为rangeTo的扩展函数。

    in的约定

    表达式 函数名
    a in b b.contains(a)

    in运算符用来检查某个对象是否属于区间(集合)。

    fun main(args: Array<String>) {
        println(1 in 1..10) // true
        println(6.6 in 1..10) // true
        println(10 in 1..10) // true
        println(11 in 1..10) // false
    }
    

    解构声明

    结构声明允许你展开单个复合值,并使用它来初始化多个(最多5个)单独的变量。

    fun main(args: Array<String>) {
        val (x, y) = Point(1,10)
        println(x) //1
        println(y) //10
    }
    

    要在解构声明中初始化每个变量,将会调用名为componentN的函数,其中N是声明中变量的位置,从1开始。对于数据类,编译器为每个在主构造方法中声明的非私有属性生成一个componentN函数,非数据类则需要我们自己添加。

    val (a, b) = p 会被转换成 val a = p.component1() val b = p.component2()

    Kotlin标准库中数组和集合已经定义了解构声明的扩展函数,比如遍历Map:

    for((key, value) in map){
      println("$key:$value")
    }
    

    invoke约定

    如果一个类中定义了使用operator修饰符的invoke方法,就可以被当作函数一样调用该类的实例。

    fun main(args: Array<String>) {
        val person = Person(18)
        person("tom")
    }
    class Person(var age:Int){
        operator fun invoke(name:String){
            println("$name,$age")
        }
    }
    

    与java互操作

    Java调用Kotlin约定好的运算符很容易,可以直接调用与之对应的函数;Kotlin调用Java时,如果Java定义了一个与Kotlin约定相匹配的函数,Kotlin可以直接使用约定的运算符调用,Java中没有operator关键字,也不需要该关键字修饰。

    相关文章

      网友评论

        本文标题:Kotlin学习笔记(四)约定(一)

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