Kotlin的操作符重载的规则是:
- 该方法使用operator修饰符
- 该方法的方法名必须被声明为特定的名称,以将对应的操作符映射为这个函数的调用
- 参数必须符合该操作符的规定,比如+的重载就不能有多于一个(不含)的参数,也不能为空参数。
举个例子,我们要重载A类的+运算符。注意三个规定(函数名、参数得符合规矩,加operator修饰):
class A {
operator fun plus(a: A) {
println("invoking plus")
}
}
这就是对+的重载了。我们可以这样调用这个函数:
val a = A() + A()
单目运算符
表达式 | 对应的函数 |
---|---|
+a | a.unaryPlus() |
-a | a.unaryMinus() |
!a | a.not() |
a++ | a.inc() |
a-- | a.dec() |
双目运算符
表达式 | 对应的函数 |
---|---|
a+b | a.plus(b) |
a-b | a.minus(b) |
a*b | a.times(b) |
a/b | a.div(b) |
a%b | a.mod(b) |
a..b | a.rangeTo(b) |
a in b | b.contains(a) |
a !in b | !b.contains(a) |
a+=b | a.plusAssign(b) |
a-=b | a.minusAssign(b) |
a*=b | a.timesAssign(b) |
a/=b | a.divAssign(b) |
a%=b | a.modAssign(b) |
类数组(Array-like)运算符
表达式 | 对应函数 |
---|---|
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) |
相等运算符
表达式 | 对应的函数 |
---|---|
a==b | a?.equals(b) ?:b === null |
a==b | !(a?.equals(b) ?:b === null) |
函数调用
表达式 | 对应的函数 |
---|---|
a(i) | a.invoke(i) |
a(i,j) | a.invoke(i,j) |
a(i_1, ..., i_n) |
a.invoke(i_1, ..., i_n)
|
扩展函数中的操作符
我们不需要去扩展我们自己的类,但是我需要去使用扩展函数扩展我们已经存在的类来让第三方的库能提供更多的操作。几个例子,我们可以去像访问List的方式去访问ViewGroup
的view:
operator fun ViewGroup.get(position: Int): View = getChildAt(position)
现在真的可以非常简单地从一个ViewGroup
中通过position得到一个view:
val container: ViewGroup = find(R.id.container) val view = container[2]
25.Comparison
//大小比较运算符 // 实现日期对象大小的比较 fun task25(date1: MyDate, date2: MyDate): Boolean { // todoTask25() return date1 < date2
}
//想要实现日期大小对比较,MyData类需要实现Comparable接口 data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int): Comparable<MyDate> { override fun compareTo(other: MyDate) = when { year != other.year -> year - other.year month != other.month -> month - other.month else -> dayOfMonth - other.dayOfMonth } }
26.InRange
//检查指定的日期是不是在某一个日期范围内 fun checkInRange(date: MyDate, first: MyDate, last: MyDate): Boolean { // todoTask26_() return date in DateRange(first, last)
}
//a in b翻译以后就是b.contains(a),所以这里的任务就转换成实现DateRange类的contains(d: MyDate):Boolean函数
operator fun contains(d: MyDate) = (d>=start && d<=endInclusive)
27.RangeTo
fun checkInRange2(date: MyDate, first: MyDate, last: MyDate): Boolean { // todoTask27() return date in first..last
}
operator fun MyDate.rangeTo(other: MyDate): DateRange = DateRange(this, other)
28.ForLoop
//对DateRange内的MyDate执行for循环,以天为迭代间隔,定义好的nextDay()方法,一次递增一天 fun MyDate.nextDay() = addTimeIntervals(DAY, 1) fun MyDate.addTimeIntervals(timeInterval: TimeInterval, number: Int) = Calendar.getInstance().run { set(year, month, dayOfMonth) add(when (timeInterval) { TimeInterval.DAY -> Calendar.DAY_OF_MONTH TimeInterval.WEEK -> Calendar.WEEK_OF_MONTH TimeInterval.YEAR -> Calendar.YEAR }, number) MyDate(get(Calendar.YEAR), get(Calendar.MONTH), get(Calendar.DATE))
}
//DateRange内的MyDate执行for循环,也就是要求DataRange实现Iterable<MyDate>接口 class DateIterator(val dateRange: DateRange) : Iterator<MyDate> { var current: MyDate = dateRange.start override fun next(): MyDate { val result = current current = current.nextDay() return result } override fun hasNext(): Boolean = current <= dateRange.endInclusive }
class DateRange( override val start: MyDate, override val endInclusive: MyDate ) : ClosedRange<MyDate>, Iterable<MyDate> { override fun iterator(): Iterator<MyDate> = DateIterator(this) override fun contains(value: MyDate): Boolean = start <= value && value <= endInclusive }
29.OperatorsOverloading
//重载MyDate的+运算符 //MyDate可以和一个时间间隔相加,所以需要实现MyDate.plus()函数,以时间间隔为参数 fun task29_1(today: MyDate): MyDate { // todoTask29() return today + YEAR + WEEK }
operator fun MyDate.plus(timeInterval: TimeInterval) = addTimeIntervals(timeInterval, 1)
//要求相加多个时间间隔,需要先实现时间间隔的*运算符 fun task29_2(today: MyDate): MyDate { // todoTask29() return today + YEAR * 2 + WEEK * 3 + DAY * 5 }
class RepeatedTimeInterval(val timeInterval: TimeInterval, val number: Int)
operator fun MyDate.plus(timeIntervals: RepeatedTimeInterval) = addTimeIntervals(timeIntervals.timeInterval, timeIntervals.number)
30.DestructuringDeclarations
data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int) fun isLeapDay(date: MyDate): Boolean { //将一个对象的所有属性一次赋值给一堆变量 //使用Destructuring Declaration,类必须声明为data类型 // todoTask30() val (year, month, dayOfMonth) = date // 29 February of a leap year return isLeapYear(year) && month == 1 && dayOfMonth == 29 } //判断是否是闰年 // Years which are multiples of four (with the exception of years divisible by 100 but not by 400) fun isLeapYear(year: Int): Boolean = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
31.invoke
class Invokable(private var invocations: Int = 0) { //如果一个类实现了invoke()这个函数,那么该类的实体对象在调用这个函数时可以省略函数名 //invoke函数必须要有operator修饰符 operator fun invoke(): Invokable { invocations++ return this } fun getNumberOfInvocations() = invocations } fun todoTask31(): Nothing = TODO( """ Task 31. Change the class 'Invokable' to count the number of invocations: for 'invokable()()()()' it should be 4. """, references = { invokable: Invokable -> }) //要求如下代码返回的结果为4,调用了3次invoke fun task31(invokable: Invokable): Int { // todoTask31() return invokable()()()().getNumberOfInvocations() }