前言:内容主要包括运算符重载、约定(支持各种运算的特殊命名函数)、委托属性,结合代码,让重载运算符相关知识简单易懂。
目录
Kotlin运算符重载及其他约定
一、概述
二、重载算数运算符
1.重载二元算数运算
2.重载复合赋值运算符
3.重载一元运算符
三、重载比较运算符
1.等号运算符:“equals”
2.排序运算符:compareTo
四、集合与区间的定义
1.通过下标来访问元素:get和set
2.in 的约定
3.rangeTo的约定
4.在for循环中使用iterator约定
五、解构声明和组件函数
在Kotlin中有很多类似于Java的特性,其中之一就是调用自己代码中定义的函数,来实现特定的语言结构。但是区别于Java,在Kotlin中,这些功能与特定的函数名相关。例如,在类中定义了一个plus函数,那么按照约定,就可以在该类的实例上使用+号来调用这个函数。在Kotlin中,我们把这种技术称为约定。
作为例子,我们实现一个Point类,来表示一个点,后面的例子基于此类:
data class Point(val x:Int,val y:Int)
在Kotlin中使用约定最直接的例子就是算数运算符。
我们要支持的第一个运算就是把两个点加到一起。这个运算需要把点的(X,Y)坐标分别加到一起。
定义一个plus运算符:
package com.houbo.shizhan
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) {
val p1 = Point(10,20)
val p2 = Point(30,40);
//会调用Point对象的plus方法
println(p1+p2)
}
运行结果:
注意:如何使用operator 关键字来声明plus函数。对于重载运算符的所有函数都需要用该关键字来标记,表示你打算把这个函数作为相应的约定实现,而不是单纯地定义一个同名函数。实际上p1+p2==p1.plus(p2)
还可以把运算符定义为扩展函数:
package com.houbo.shizhan
data class Point(val x:Int,val y:Int){}
//定义为扩展函数
operator fun Point.plus(other: Point):Point{
return Point(x+other.x,y+other.y)
}
fun main(args: Array) {
val p1 = Point(10,20)
val p2 = Point(30,40);
//会调用Point对象的plus方法
println(p1+p2)
}
下面列举二元运算符及其对应的函数名:
表达式 | 函数名 |
a*b | times |
a/b | div |
a%b | mod |
a+b | plus |
a-b | minus |
定义一个运算数类型不同的运算符:
package com.houbo.shizhan
data class Point(val x:Int,val y:Int){}
//定义为扩展函数
operator fun Point.times(scale:Double):Point{
return Point((this.x*scale).toInt(),(this.y*scale).toInt())
}
fun main(args: Array) {
val p1 = Point(10,20)
println(p1*0.5)
}
运行结果:
定义一个返回不同类型的运算符:
package com.houbo.shizhan
data class Point(val x:Int,val y:Int){}
//定义为扩展函数
operator fun Char.times(count:Int):String{
return this.toString().repeat(count)
}
fun main(args: Array) {
val char1 = 'a'
println(char1*3)
}
运行结果:
注意:没有用于位运算的特殊运算符
通常情况下,当你定义plus之类的运算符时,Kotlin不止支持+操作,也支持+=。像+=、-=等这些运算符被称为复合赋值运算符。
package com.houbo.shizhan
data class Point(val x:Int,val y:Int){}
//定义为扩展函数
operator fun Point.plus(other: Point):Point{
return Point(x+other.x,y+other.y)
}
fun main(args: Array) {
var p1 = Point(10,20)
p1 += Point(30,40)
println(p1)
}
运行结果:
在一些情况下,定义+=运算可以修改变量所引用的对象,并不会创建新的引用。
如果你定义了一个返回值为Unit,名为plusAssign的函数,Kotlin在用到+=的地方调用它,其他二元运算类推。
operator fun MutableCollection.plusAssign(element: T){
this.add(element)
}
fun main(args: Array) {
val number = ArrayList()
number += 66
println(number)
}
operator fun MutableCollection.plus(element: T):Any{
return this.add(element)
}
fun main(args: Array) {
val number = ArrayList()
number += 66
println(number)
}
运行结果:
重载一元运算符的方式跟前面相似:用预定义的函数名让operator进行修饰。
operator fun Point.unaryMinus():Point{
return Point(-this.x,-this.y)
}
fun main(args: Array) {
val p1 = Point(10,20)
println(-p1)
}
运行结果:
下面列举一元运算符对应的函数名:
表达式 | 函数名 |
+a | unaryPlus |
-a | unaryMinus |
!a | not |
++a a++ | inc |
--a a-- | dec |
定义一个自增函数:
operator fun BigDecimal.inc() = this+BigDecimal.ONE
fun main(args: Array) {
var x = BigDecimal.ZERO
println(x++)
println(++x)
}
运行结果:
与算数运算符一样,在Kotlin中,可以对任何对象使用比较运算符(==、!=、>、<等)
等式校验==被转换成equals函数,以及null的校验:
package com.houbo.shizhan
data class Point(val x:Int,val y:Int){
override fun equals(other: Any?): Boolean {
if (this===other) return true
if (other!is Point) return false
return other.x==x && other.y==y
}
}
fun main(args: Array) {
println(Point(10,20)== Point(10,20))
println(Point(10,20)!= Point(5,5))
println(null==Point(3,3))
}
运算结果:
注意:===号类似于java中==号,用于判断是否是同一个对象。
Kotlin支持Comparable接口。但是接口中定义的compareTo方法可以按约定调用,比较运算符最终被转化成compareTo。
实现compareTo方法:
class Person1(val firstName:String,val lastName: String):Comparable{
override fun compareTo(other: Person1): Int {
return compareValuesBy(this,other,Person1::firstName,Person1::lastName)
}
}
fun main(args: Array) {
val p1 = Person1("bob","smith")
val p2 = Person1("Alice","jhon")
println(p1
运行结果:
疑问?为什么equals和compareTo方法不用operator修饰呢?因为operator修饰符已经在基类接口中定义了,因此重写时无需重复定义。
处理集合最常见的一些操作是通过下标来获取或者设置值,可以使用in运算符来检查一个元素是否在集合中。
实现get约定:
operator fun Point.get(index:Int):Int{
return when(index){
0 -> this.x
1 -> this.y
else -> throw Exception("ex")
}
}
fun main(args: Array) {
val p = Point(10,20)
println(p[1])
}
运行结果:
实现set约定:
operator fun Point.set(index: Int,value:Int){
when(index){
0 -> this.x = value
1 -> this.y = value
else -> throw Exception("ex")
}
}
fun main(args: Array) {
val p = Point(10,20)
p[0] = 40
println(p)
}
运行结果:
in运算符用于检查一个元素是否在某个集合中,相应的函数时contains。
实现in约定:
data class Rectangle1(val upperLeft:Point,val lowerRight:Point)
operator fun Rectangle1.contains(p:Point):Boolean{
return p.x in upperLeft.x until lowerRight.x && p.y in upperLeft.y until lowerRight.y
}
fun main(args: Array) {
val rec = Rectangle1(Point(10,20), Point(50,50))
println(Point(20,30) in rec)
}
运行结果:
要创建一个区间,使用..语法,运算符调用的是rangeTo方法。
fun main(args: Array) {
val now = LocalDate.now()
val vocation = now..now.plusDays(10)
println(now.plusWeeks(1) in vocation)
}
运行结果:
实现日期区间迭代器:
operator fun ClosedRange.iterator():Iterator =
object : Iterator{
var current = start
override fun hasNext(): Boolean {
return current <= endInclusive
}
override fun next(): LocalDate {
return current.apply { current = plusDays(1) }
}
}
fun main(args: Array) {
val newYear = LocalDate.ofYearDay(2017,1)
val daysOff = newYear.minusDays(1)..newYear
for (dayOff in daysOff){
println(dayOff)
}
}
运行结果:
这个功能允许你展开单个复合值,并用它来初始化多个变量,解构声明的函数是componentN
fun main(args: Array) {
var p = Point(10,20)
var (x,y) = p
println("x:$x y:$y")
}
运行结果:
使用解构声明来返回多个值:
data class NameComponenets(val name:String,val extension:String)
fun splitFileName(fullName:String):NameComponenets{
val result = fullName.split('.',limit = 2)
return NameComponenets(result[0],result[1])
}
fun main(args: Array) {
val (name,ext) = splitFileName("test.kt")
println(name)
println(ext)
}
运行结果:
一个解构看似一个对象,实际上包含多个变量,需要注意的是,类必须data class才会默认生成componenetN函数。