Kotlin 允许使用对应名称的函数来重载一些标准的数学运算,但是不能定义自己的运算符
Kotlin 中的约定: 为 不同操作符 规范 操作符重载 的约定。例如,如果在你的类中定义了一个名为 plus 的特殊方法,那么按照约定,就可以在该类的实例上使用 + 运算符。
Kotlin 允许我们为自己的类型提供预定义的一组操作符的实现。这些操作符具有固定的符号表示 (如 + 或 *)和固定的优先级。为实现这样的操作符,我们为相应的类型(即二元操作符左侧的类型和一元操作符的参数类型)提供了一个固定名字的成员函数或扩展函数。 重载操作符的函数需要用 operator 修饰符标记。
一、重载一元运算符
1、一元前缀操作符:
表达式 | 翻译为 |
---|---|
+a | a.unaryPlus() |
-a | a.unaryMinus() |
!a | a.not() |
当编译器处理例如表达式 +a 时,它执行以下步骤:
1)确定 a 的类型,令其为 T
2)为接收者 T 查找一个带有 operator 修饰符的无参函数 unaryPlus(),即成员函数或扩展函数
3)如果函数不存在或不明确,则导致编译错误
4)如果函数存在且其返回类型为 R,那么表达式 +a 具有类型 R
实例:
data class Point(val x: Int, val y: Int)
operator fun Point.unaryMinus() = Point(-x, -y)
val point = Point(10, 20)
fun main() {
println(-point) // 输出“Point(x=-10, y=-20)”
}
2、递增与递减:
表达式 | 翻译为 |
---|---|
a++, ++a | a.inc() |
a–, --a | a.dec() |
当编译器处理例如表达式 a++ 时,它执行以下步骤:
1)确定 a 的类型,令其为 T
2)查找一个适用于类型为 T 的接收者的、带有 operator 修饰符的无参函数 inc()
3)检测函数的返回类型是 T 的子类型
二、重载二元操作
1、二元算术运算符
表达式 | 翻译为 |
---|---|
a + b | a.plus(b) |
a - b | a.minus(b) |
a * b | a.times(b) |
a / b | a.div(b) |
a % b | a.rem(b)、a.mod(b)【kotlin1.1中已弃用】 |
- 自定义类型的运算符,基本上和标准数字类型的运算符有着相同的优先级。*、/ 和 % 具有相同的优先级,高于 + 和 - 运算符的优先级。
- 在定义一个运算符时,不要求两个运算数是相同类型
- Kotlin 运算符不会自动支持交换性(交换运算符的左右两边),即 p1.5 和 1.5p 是不同的
实例:
写法1(成员函数):
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)
println(p1, p2) //Point(x=40, y=60)
}
写法2(扩展函数):
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)
println(p1, p2) //Point(x=40, y=60)
}
2、复合赋值操作符
表达式 | 翻译为 |
---|---|
a += b | a.plusAssign(b) |
a -= b | a.minusAssign(b) |
a *= b | a.timesAssign(b) |
a /= b | a.divAssign(b) |
a %= b | a.remAssign(b)、a.modAssign(b)【modAssign已弃用】 |
注意:尽量不要给一个类同时添加 plus 和 plusAssign 运算,这种情况下,编译器会报错。
3、“ ..” 操作符
表达式 | 翻译为 |
---|---|
a..b | a.rangeTo(b) |
- 1..10,代表所有从1到10的数字
- rangeTo 函数返回一个区间
- 可以为任何类定义 rangeTo 函数,但是,如果该类实现了 Comparable 接口,那么可以直接使用 Kotlin 标准库为 Comparable 接口提供的 rangeTo 函数来创建一个区间,即不用自己定义 rangeTo 函数了(注意,此时可用来检测其它一些元素是否属于该区间;但如果要遍历该区间,还需实现Iterable)
fun main() {
val start = MyDate(2016, 11, 11)
val end = MyDate(2016, 11, 30)
val other = MyDate(2017, 11, 23)
println(other in start..end)
}
data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int) : Comparable<MyDate> {
override operator fun compareTo(other: MyDate): Int {
return if (year != other.year) {
year - other.year
} else if (month != other.month) {
month - other.month
} else {
dayOfMonth - other.dayOfMonth
}
}
}
遍历区间(在 for 循环中使用 iterator 的约定):
fun main() {
val start = MyDate(2016, 11, 11)
val end = MyDate(2016, 11, 30)
val other = MyDate(2017, 11, 23)
println(other in start..end)
for (date in start..end) {
println(date)
}
}
data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int) : Comparable<MyDate> {
override operator fun compareTo(other: MyDate): Int {
return if (year != other.year) {
year - other.year
} else if (month != other.month) {
month - other.month
} else {
dayOfMonth - other.dayOfMonth
}
}
operator fun rangeTo(other: MyDate): DateRange {
return DateRange(this, other)
}
fun addOneDay(): MyDate {
val c = Calendar.getInstance()
c.set(this.year, this.month, this.dayOfMonth)
c.add(Calendar.DAY_OF_MONTH, 1)
return MyDate(c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH))
}
class DateRange(override val start: MyDate, override val endInclusive: MyDate) : Iterable<MyDate>, ClosedRange<MyDate> {
override fun iterator(): Iterator<MyDate> = object : Iterator<MyDate> {
var hasNext = start <= endInclusive //这里使用了 CompareTo 约定
var next = if (hasNext) start else endInclusive
override fun hasNext(): Boolean = hasNext
override fun next(): MyDate {
val result = next
next = next.addOneDay()
hasNext = next <= endInclusive
return result
}
}
}
}
4、“ in ” 操作符
表达式 | 翻译为 |
---|---|
a in b | b.contains(a) |
a !in b | !b.contains(a) |
data class Rectangle(val upperLeft:Point, val lowerRight:Point)
operator fun Rectangle.contains(p:Point):Boolean{
//注意,在contains方法中是可以使用 in 的
return p.x in upperLeft.x until lowerRight.x &&
p.y in upperLeft.y until lowerRight.y
}
5、索引访问操作符
表达式 | 翻译为 |
---|---|
a[i] | a.get(i) |
a[i, j] | a.get(i, j) |
a[i_1, …, i_n] | a.get(i_1, …, i_n) |
a[i] = b | a.set(i, b) |
a[i, j] = b | a.set(i, j, b) |
a[i_1, …, i_n] = b | a.set(i_1, …, i_n, b) |
6、 “==” 操作符
表达式 | 翻译为 |
---|---|
a == b | a?.equals(b) ?: (b === null) |
a != b | !(a?.equals(b) ?: (b === null)) |
手动实现 equals 函数
class Point(val x:Int, val y:Int) {
override fun equals(obj: Any?):Boolean{
if( obj === this) return true
if( obj !is Point) return false
return obj.x==x && obj.y==y
}
}
注意:
1)equals方法是在Any类中定义的,要标记为 override
2)equals 不能实现为扩展函数,因为继承自 Any 类的实现始终优先于扩展函数
7、 比较操作符(>、<、>=、<=)
表达式 | 翻译为 |
---|---|
a > b | a.compareTo(b) > 0 |
a < b | a.compareTo(b) < 0 |
a >= b | a.compareTo(b) >= 0 |
a <= b | a.compareTo(b) <= 0 |
compareTo 的返回类型为 Int
8、调用操作符
表达式 | 翻译为 |
---|---|
a() | a.invoke() |
a(i) | a.invoke(i) |
a(i, j) | a.invoke(i, j) |
a(i_1, …, i_n) | a.invoke(i_1, …, i_n) |
参考: