kotlin 重载运算符

一、二元运算符的重载

1、常见的运算符有:加、减、乘、除、求余;我们要重载这些运算符的操作

这里以加法重载运算符为例

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

如上在Point类中增加了operator标记,表示该函数是重载了plus; 重载了plus之后就能执行两个Point的相加: val point3 = point1 + point2

    val p1 = Point(10, 20)
    val p2 = Point(20, 30)
    println(p1 + p2) // Point(x=30, y=50)

 

为何这里重载了plus之后就可以执行两个Point的相加? 其实这个是kotlin的约定

如下是加、减、乘、除、求余各个表达式对应的重载运算符

这里的运算符跟咱们平时使用的运算符加减乘除一样有优先级,如下:

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

    val p1 = Point(10, 20)
    val p2 = Point(20, 30)
    val p3 = Point(2, 3)
    println(p1 + p2 * p3) // Point(x=50, y=110)

也可以在类外面定义即把它定义为扩展函数

operator fun Point.minus(other: Point): Point {
    return Point(x - other.x, y - other.y)
}
    val p1 = Point(10, 20)
    val p2 = Point(20, 30)
    println(p1 - p2)

 

2、关于上述的运算符对应的 +=、-=。。。。。

这里以加为例子,对应的运算符重载可以是plus 也可以是plusAsign

但是plus对应的方式是返回对象的赋值,plusAsign对应的方式是内部对象的赋值

data class Point(var x: Int, var y: Int) {
    operator fun plus(other: Point): Point { // 这里以返回对象的方式
        return Point(x + other.x, y + other.y)
    }
    operator fun plusAssign(other: Point) { // 这里以改变内部参数值的方式
        this.x += x
        this.y += y
    }
}

如下是调用方式

    var p1 = Point(10, 20)
    val p2 = Point(20, 30)

    p1 += p2 // 这里会报错,因为p1是var的方式,那么上述的两个重载都是能够调用

    // 对应的解决方案是:让p1为val的类型
    val p1 = Point(10, 20)
    val p2 = Point(20, 30)
    p1 += p2 // 这里调用的会是plusAssign,在p1的内部修改x和y;因为p1是val的方式则不允许用结果返回的方式重新给p1赋值

如下是 a+=b 的调用逻辑

 

 

二、重载一元运算符

1、跟二元运算符类似,如下是一元运算符表达式对应的函数名

这里有疑问的应该就前加和后加,其实从上述的前加和后加都调用同一个函数inc

operator fun BigDecimal.inc() = this + BigDecimal.TEN // 一个自加,加10
    var bd = BigDecimal.ZERO
    println(bd++) // 0
    println(++bd) // 20

如上是前自加和后自加,跟咱们平时返回的一致

 

三、重载比较运算符

1、等号运算符:equals

具体的判断方式为

a == b =>  a?.equals(b) ?: (b == null)

如果a为空则判断b是否为空

注意:这里的equals不能被重载为扩展函数,因为这个equals是定义在any中,类中的函数始终优先于扩展函数。所以即使重载为扩展函数也是不会被调用到。

 

2、排序运算符: compareTo

实现接口Comparable对应的函数compareTo

a.compareTo(b)

如果a大于b则返回大于0的数,如果a等于b则返回0,如果a小于b则返回小于0的数

class Person(val firstName: String, val lastName: String): Comparable<Person> {
    override fun compareTo(other: Person): Int {
        return compareValuesBy(this, other, Person::firstName, Person::lastName) // 这里传入的参数是按照顺序,比较的参数也是按照顺序
    }
}

 

四、通过下标访问元素:get和set

1、val value = map[key] 类似这种方式即:map再加方括号的形式

因为有取值也有设置值所以区分了get和set

operator fun Point.get(index: Int): Int {
    return when(index) {
        0 -> x
        1 -> y
        else -> throw IndexOutOfBoundsException("kkqqq")
    }
}

operator fun Point.set(index: Int, value: Int) {
    when(index) {
        0 -> x = value
        1 -> y = value
        else -> throw IndexOutOfBoundsException("out")
    }
}

    val p = Point(10, 20)
    p[1] = 30 // 这里其实调用的是上面扩展函数的set
    println(p[1]) // 这里调用的是上面扩展函数的get,打印的结果是30

如上是增加get和set的重载方法,这里只是刚好用数字作为index,在实际运用过程中也可以有字符串等

也可以有二维的,三维的等等;比如获取二维数组的值 matrix[x, y]

operator fun Matrix.get(row: Int, col Int): String

 

五、通过in的方式访问元素:contains

1、判断某个参数是否在某个区间里面:a in b 这样子的判断很符合自然语言的表达方式

kotlin支持这种方式,只要在b的类里面重载contains表达式

如下的例子是判断点是否在长方形里面

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

然后调用的函数为

val p = Point(10, 20)
val rect = Rectangle(Point(10, 20), Point(50, 60))
println(p in rect) // 这里使用in的判断方式

上面使用扩展函数Rectangle.contains 重载contains

在内部一个使用了until 另一个使用了.. 的方式表达区间的方式

until: 左闭右开 [upperLeft.x, lowerRight.x)

..: 两个点是两边都闭合:[upperLeft.y, lowerRight.y]

具体底层的实现是:until 使用了中缀表达式的方式:infix,.. 使用了重载rangeTo的方式

posted @ 2023-11-05 23:45  LCAC  阅读(291)  评论(0编辑  收藏  举报