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

Kotlin 允许使用对应名称的函数来重载一些标准的数学运算,但是不能定义自己的运算符

Kotlin 中的约定: 为 不同操作符 规范 操作符重载 的约定。例如,如果在你的类中定义了一个名为 plus 的特殊方法,那么按照约定,就可以在该类的实例上使用 + 运算符。

Kotlin 允许我们为自己的类型提供预定义的一组操作符的实现。这些操作符具有固定的符号表示 (如 + 或 *)和固定的优先级。为实现这样的操作符,我们为相应的类型(即二元操作符左侧的类型和一元操作符的参数类型)提供了一个固定名字的成员函数或扩展函数。 重载操作符的函数需要用 operator 修饰符标记。

一、重载一元运算符

1、一元前缀操作符:

表达式翻译为
+aa.unaryPlus()
-aa.unaryMinus()
!aa.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++, ++aa.inc()
a–, --aa.dec()

当编译器处理例如表达式 a++ 时,它执行以下步骤:
1)确定 a 的类型,令其为 T
2)查找一个适用于类型为 T 的接收者的、带有 operator 修饰符的无参函数 inc()
3)检测函数的返回类型是 T 的子类型

二、重载二元操作

1、二元算术运算符

表达式翻译为
a + ba.plus(b)
a - ba.minus(b)
a * ba.times(b)
a / ba.div(b)
a % ba.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 += ba.plusAssign(b)
a -= ba.minusAssign(b)
a *= ba.timesAssign(b)
a /= ba.divAssign(b)
a %= ba.remAssign(b)、a.modAssign(b)【modAssign已弃用】

注意:尽量不要给一个类同时添加 plus 和 plusAssign 运算,这种情况下,编译器会报错。

3、“ ..” 操作符

表达式翻译为
a..ba.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 bb.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] = ba.set(i, b)
a[i, j] = ba.set(i, j, b)
a[i_1, …, i_n] = ba.set(i_1, …, i_n, b)

6、 “==” 操作符

表达式翻译为
a == ba?.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 > ba.compareTo(b) > 0
a < ba.compareTo(b) < 0
a >= ba.compareTo(b) >= 0
a <= ba.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知识归纳(九) —— 约定

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值