Kotlin基础学习(十八)—— Kotlin 运算符重载与约定

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)

参考:

操作符重载

自定义 rangeTo 操作符

Kotlin知识归纳(九) —— 约定

你可能感兴趣的:(Kotlin基础,kotlin,学习,android)