Kotlin运算符重载及其他约定

Kotlin运算符重载及其他约定

前言:内容主要包括运算符重载、约定(支持各种运算的特殊命名函数)、委托属性,结合代码,让重载运算符相关知识简单易懂。


目录

Kotlin运算符重载及其他约定

一、概述

二、重载算数运算符

1.重载二元算数运算

2.重载复合赋值运算符

3.重载一元运算符

三、重载比较运算符

1.等号运算符:“equals”

2.排序运算符:compareTo

四、集合与区间的定义

1.通过下标来访问元素:get和set

2.in 的约定

3.rangeTo的约定

4.在for循环中使用iterator约定

五、解构声明和组件函数


 

一、概述

在Kotlin中有很多类似于Java的特性,其中之一就是调用自己代码中定义的函数,来实现特定的语言结构。但是区别于Java,在Kotlin中,这些功能与特定的函数名相关。例如,在类中定义了一个plus函数,那么按照约定,就可以在该类的实例上使用+号来调用这个函数。在Kotlin中,我们把这种技术称为约定。

作为例子,我们实现一个Point类,来表示一个点,后面的例子基于此类:

data class Point(val x:Int,val y:Int)

二、重载算数运算符

在Kotlin中使用约定最直接的例子就是算数运算符。

1.重载二元算数运算

我们要支持的第一个运算就是把两个点加到一起。这个运算需要把点的(X,Y)坐标分别加到一起。

定义一个plus运算符:

package com.houbo.shizhan

data class Point(val x:Int,val y:Int){
    operator fun plus(other: Point):Point{
        return Point(x+other.x,y+other.y)
    }
}

fun main(args: Array<String>) {
    val p1 = Point(10,20)
    val p2 = Point(30,40);
    //会调用Point对象的plus方法
    println(p1+p2)
}

运行结果:

注意:如何使用operator 关键字来声明plus函数。对于重载运算符的所有函数都需要用该关键字来标记,表示你打算把这个函数作为相应的约定实现,而不是单纯地定义一个同名函数。实际上p1+p2==p1.plus(p2)

还可以把运算符定义为扩展函数:

package com.houbo.shizhan

data class Point(val x:Int,val y:Int){}
//定义为扩展函数
operator fun Point.plus(other: Point):Point{
    return Point(x+other.x,y+other.y)
}

fun main(args: Array<String>) {
    val p1 = Point(10,20)
    val p2 = Point(30,40);
    //会调用Point对象的plus方法
    println(p1+p2)
}

下面列举二元运算符及其对应的函数名: 

表达式函数名
a*btimes
a/bdiv
a%bmod
a+bplus
a-bminus

 

 

 

 

 

 

 

定义一个运算数类型不同的运算符:

package com.houbo.shizhan

data class Point(val x:Int,val y:Int){}
//定义为扩展函数
operator fun Point.times(scale:Double):Point{
    return Point((this.x*scale).toInt(),(this.y*scale).toInt())
}

fun main(args: Array<String>) {
    val p1 = Point(10,20)
    println(p1*0.5)
}

运行结果:

定义一个返回不同类型的运算符:

package com.houbo.shizhan

data class Point(val x:Int,val y:Int){}
//定义为扩展函数
operator fun Char.times(count:Int):String{
    return this.toString().repeat(count)
}

fun main(args: Array<String>) {
    val char1 = 'a'
    println(char1*3)
}

运行结果:

注意:没有用于位运算的特殊运算符

2.重载复合赋值运算符

通常情况下,当你定义plus之类的运算符时,Kotlin不止支持+操作,也支持+=。像+=、-=等这些运算符被称为复合赋值运算符。

package com.houbo.shizhan

data class Point(val x:Int,val y:Int){}
//定义为扩展函数
operator fun Point.plus(other: Point):Point{
    return Point(x+other.x,y+other.y)
}
fun main(args: Array<String>) {
    var p1 = Point(10,20)
    p1 += Point(30,40)
    println(p1)
}

运行结果:

在一些情况下,定义+=运算可以修改变量所引用的对象,并不会创建新的引用。

如果你定义了一个返回值为Unit,名为plusAssign的函数,Kotlin在用到+=的地方调用它,其他二元运算类推。

operator fun <T> MutableCollection<T>.plusAssign(element: T){
    this.add(element)
}
fun main(args: Array<String>) {
    val number = ArrayList<Int>()
    number += 66
    println(number)
}
operator fun <T> MutableCollection<T>.plus(element: T):Any{
    return this.add(element)
}
fun main(args: Array<String>) {
    val number = ArrayList<Int>()
    number += 66
    println(number)
}

运行结果:

3.重载一元运算符

重载一元运算符的方式跟前面相似:用预定义的函数名让operator进行修饰。

operator fun Point.unaryMinus():Point{
    return Point(-this.x,-this.y)
}
fun main(args: Array<String>) {
    val p1 = Point(10,20)
    println(-p1)
}

运行结果:

下面列举一元运算符对应的函数名:

表达式函数名
+aunaryPlus
-aunaryMinus
!anot
++a  a++inc
--a  a--dec

 

 

 

 

 

 

 

定义一个自增函数:

operator fun BigDecimal.inc() = this+BigDecimal.ONE

fun main(args: Array<String>) {
    var x = BigDecimal.ZERO
    println(x++)
    println(++x)
}

运行结果:

三、重载比较运算符

与算数运算符一样,在Kotlin中,可以对任何对象使用比较运算符(==、!=、>、<等)

1.等号运算符:“equals”

等式校验==被转换成equals函数,以及null的校验:

package com.houbo.shizhan

data class Point(val x:Int,val y:Int){
    override fun equals(other: Any?): Boolean {
        if (this===other) return true
        if (other!is Point) return false
        return other.x==x && other.y==y
    }
}

fun main(args: Array<String>) {
    println(Point(10,20)== Point(10,20))
    println(Point(10,20)!= Point(5,5))
    println(null==Point(3,3))
}

运算结果:

注意:===号类似于java中==号,用于判断是否是同一个对象。

2.排序运算符:compareTo

Kotlin支持Comparable接口。但是接口中定义的compareTo方法可以按约定调用,比较运算符最终被转化成compareTo。

实现compareTo方法:

class Person1(val firstName:String,val lastName: String):Comparable<Person1>{
    override fun compareTo(other: Person1): Int {
        return compareValuesBy(this,other,Person1::firstName,Person1::lastName)
    }
}
fun main(args: Array<String>) {
    val p1 = Person1("bob","smith")
    val p2 = Person1("Alice","jhon")
    println(p1<p2)
}

运行结果:

疑问?为什么equals和compareTo方法不用operator修饰呢?因为operator修饰符已经在基类接口中定义了,因此重写时无需重复定义。

四、集合与区间的定义

处理集合最常见的一些操作是通过下标来获取或者设置值,可以使用in运算符来检查一个元素是否在集合中。

1.通过下标来访问元素:get和set

实现get约定:

operator fun Point.get(index:Int):Int{
    return when(index){
        0 -> this.x
        1 -> this.y
        else -> throw Exception("ex")
    }
}
fun main(args: Array<String>) {
    val p = Point(10,20)
    println(p[1])
}

运行结果:

实现set约定:

operator fun Point.set(index: Int,value:Int){
    when(index){
        0 -> this.x = value
        1 -> this.y = value
        else -> throw Exception("ex")
    }
}
fun main(args: Array<String>) {
    val p = Point(10,20)
    p[0] = 40
    println(p)
}

运行结果:

2.in 的约定

in运算符用于检查一个元素是否在某个集合中,相应的函数时contains。

实现in约定:

data class Rectangle1(val upperLeft:Point,val lowerRight:Point)
operator fun Rectangle1.contains(p:Point):Boolean{
    return p.x in upperLeft.x until lowerRight.x && p.y in upperLeft.y until lowerRight.y
}

fun main(args: Array<String>) {
    val rec = Rectangle1(Point(10,20), Point(50,50))
    println(Point(20,30) in rec)
}

运行结果:

3.rangeTo的约定

要创建一个区间,使用..语法,运算符调用的是rangeTo方法。

fun main(args: Array<String>) {
    val now = LocalDate.now()
    val vocation = now..now.plusDays(10)
    println(now.plusWeeks(1) in vocation)
}

运行结果:

4.在for循环中使用iterator约定

实现日期区间迭代器:

operator fun ClosedRange<LocalDate>.iterator():Iterator<LocalDate> =
        object : Iterator<LocalDate>{
            var current = start
            override fun hasNext(): Boolean {
                return current <= endInclusive
            }

            override fun next(): LocalDate {
                return current.apply { current = plusDays(1) }
            }
        }

fun main(args: Array<String>) {
    val newYear = LocalDate.ofYearDay(2017,1)
    val daysOff = newYear.minusDays(1)..newYear
    for (dayOff in daysOff){
        println(dayOff)
    }
}

运行结果:

五、解构声明和组件函数

这个功能允许你展开单个复合值,并用它来初始化多个变量,解构声明的函数是componentN

fun main(args: Array<String>) {
    var p = Point(10,20)
    var (x,y) = p
    println("x:$x y:$y")
}

运行结果:

使用解构声明来返回多个值:

data class NameComponenets(val name:String,val extension:String)
fun splitFileName(fullName:String):NameComponenets{
    val result = fullName.split('.',limit = 2)
    return NameComponenets(result[0],result[1])
}

fun main(args: Array<String>) {
    val (name,ext) = splitFileName("test.kt")
    println(name)
    println(ext)
}

运行结果:

一个解构看似一个对象,实际上包含多个变量,需要注意的是,类必须data class才会默认生成componenetN函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值