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的方式