内容简介
在 C 或 C++ 中是有操作符重载的概念的。在 Java 中我们认知的 +-*/ 只能在数字类型中做计算使用。而在 Kotlin 中我们可以通过操作符重载的形式改变其含义。
重载操作符的运用
其实在 Kotlin
中很多类型都已帮我们默认定义了一些操作符重载,为我们后续开发带来极大的遍历。我们可以重载一些常见的操作符,如 +-*/
让我们代码可读性更强。
曾经我接到一个任务,将一段 Python
代码翻译成 Kotlin
代码, Python
中可以对矩阵进行 +*/
的运算,而 Kotlin
中只有对应的方法 plus
times
div
的方法,我就是通过 Kotlin
的操作符重载的功能,让其翻译的最终代码和 Python
写法上尽量保持一致。
数组相加
我定义了 2
个不可变数组,然后通过 +
将 2
个数组合并了,在 Java
的世界中,是不存在数组相加的情况。
fun main() {
/**
* 创建2个数组
*/
val array1 = arrayOf("1", "2", "3")
val array2 = arrayOf("4", "5", "6")
val array3 = array1 + array2
/**
* 遍历输出
*/
array3.forEach(::println)
}
输出结果:
1
2
3
4
5
6
List集合相加减
上面看了数组相加,集合可以吗?想一想,当然是可以的啦!我们来看下吧!
fun main() {
/**
* 创建2个可变集合
*/
val list1 = mutableListOf<String>("1", "2", "3")
val list2 = mutableListOf("1", "5", "6")
/**
* 集合相加
*/
val list3 = list1 + list2
/**
* 遍历输出
*/
list3.forEach(::println)
println("========减法===========")
/**
* 集合相减
*/
val minusResult = list1 - list2
/**
* 遍历输出减法
*/
minusResult.forEach(::println)
}
输出结果:
1
2
3
1
5
6
========减法===========
2
3
从结果可以看出,相加是合并 2
个 List
集合。相减含义是去除减数在被减数中存在的元素。
Map
也是支持相加减的,有兴趣的可以自己去尝试。当然Kotlin
提供的操作符重载方法还有很多,我只是随便举了个例子,其他的大家自行挖掘吧。
如何自定义重载操作符
如果想重载操作符,只需要通过关键词 operator
修饰对应方法即可,并且重载的操作符对应一个固定昵称的方法。
举个????,如果我想为我自己定义的对象重载 +
的操作符,只需要通过 operator
修饰 plus
方法即可(+
操作符对应的就是 plus
方法)。
/**
* 程序员实体
*/
data class CodePeople(val name: String, val age: Int)
/**
* UI实体
*/
data class UiPeople(val name: String, val age: Int)
/**
* 团队
*/
data class Tream(
val codes: MutableList<CodePeople> = mutableListOf()
, val uis: MutableList<UiPeople> = mutableListOf()
) {
/**
* 重载 + 团队,增加程序员方法
*/
operator fun plus(code: CodePeople) {
codes + code
}
/**
* 重载 + 团队,增加 UI 方法
*/
operator fun plus(ui: UiPeople) {
uis + ui
}
}
/**
* 测试
*/
fun main() {
val tream = Tream()
/**
* 可以看到可以调用 + 了
*/
tream + CodePeople("阿文", 18)
tream + UiPeople("小丽", 18)
}
通过上面的代码,我们可以注意到,在 Tream
类中重载了 2
个 plus
方法,对应的行参就是要被加的类型。其实看到这里大家应该能猜想到,重载操作符的实现原理。其实就是 Kotlin
编译器,编译时将 +
替换成调用 plus
方法。
其实我上面的例子并不是最佳写法。一般来说,重载操作符都以扩展函数的形式去定义。这样我们不光可以为自己的类做重载操作符操作,也可以为系统的类做重载操作符操作。
举个例子:
/**
* andorid viewGroup 没有 + 操作符的的含义
* 通过扩展函数的形式编写重载操作符
*/
operator fun ViewGroup.plus(view: View): ViewGroup {
addView(view)
return this
}
fun test(context: Context) {
val frameLayout = FrameLayout(context)
val textView = TextView(context)
val textView2 = TextView(context)
/**
* 可以连续为 ViewGroup 对象,通过 + 的方式,添加 View
*/
frameLayout + textView + textView2
}
可重载的操作符
看了上面的重载操作符是不是感觉很方便呀?我们只需要通过 operator
修饰对应的方法即可。那么对应哪些操作符可以重载呢?对应的方法名又是什么呢?接下来统计下可以重载的函数。
一元操作符
操作符 | 对应重载函数 |
---|---|
+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.rem(b)、 a.mod(b) (已弃用) |
a..b | a.rangeTo(b) |
in操作符
操作符 | 对应重载函数 |
---|---|
a in b | b.contains(a) |
a !in b | !b.contains(a) |
索引操作符
操作符 | 对应重载函数 |
---|---|
a[i] | a.get(i) |
a[i, j] | a.get(i, j) |
a[i1, ……, in] | a.get(i1, ……, in) |
a[i] = b | a.set(i, b) |
a[i, j] = b | a.set(i, j, b) |
a[i1, ……, in] = b | a.set(i1, ……, in, b) |
调用操作符
操作符 | 对应重载函数 |
---|---|
a() | a.invoke() |
a(i) | a.invoke(i) |
a(i, j) | a.invoke(i, j) |
a(i1, ……, in) | a.invoke(i1, ……, in) |
广义赋值
操作符 | 对应重载函数 |
---|---|
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)(已弃用) |
相等与不等操作符
操作符 | 对应重载函数 |
---|---|
a == b | a?.equals(b) ?: (b === null) |
a != b | !(a?.equals(b) ?: (b === null)) |
比较操作符
操作符 | 对应重载函数 |
---|---|
a > b | a.compareTo(b) > 0 |
a < b | a.compareTo(b) < 0 |
a >= b | a.compareTo(b) >= 0 |
a <= b | a.compareTo(b) <= 0 |
中缀表达式
上面说过重载操作符,都是重载已经存在的操作符。那我们能不能类似自定义一个操作符呢?比如将一些无意义的关键词变的有意义?中缀表达式可以完成这个功能。
中缀表达式的运用
在说中缀表达式之前,我们回顾下集合篇的 Map
。我们在定义可变 Map
的时候,定义时初始化元素使用了 to
,这个 to
是什么意思呢?
fun main() {
val results = mutableMapOf<String, Int>("阿文" to 18, "小丽" to 18)
}
其实这里面的 to 就是一个中缀表达式,将 to 关键词从无意义变成了有意义。
看下 to
的定义,可以看到为任意类型增加了一个 to
的扩展方法,且方法通过 infix
修饰,返回 Pair
对象。 mutableMapOf
可以将 Pair
类型的对象增加成元素(其实就是 key
和 value
结构)。
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
自定义中缀表达式
通过前面的 to
讲解。我们知道了中缀表达式只需要通过 infix
修饰函数即可(一般来说,中缀表达式也是通过扩展函数的形式去创建)。
我们来举个例子。
/**
* 程序员实体
*/
data class CodePeople(val name: String, val age: Int)
/**
* UI实体
*/
data class UiPeople(val name: String, val age: Int)
/**
* 团队
*/
data class Tream(
val codes: MutableList<CodePeople> = mutableListOf()
, val uis: MutableList<UiPeople> = mutableListOf()
)
/**
* 通过扩展函数
* 中缀表达式 merge 合并2个Tream
*/
infix fun Tream.merge(tram: Tream) {
/**
* 复习下操作符重载
*/
codes + tram.codes
uis + tram.codes
}
/**
* 测试
*/
fun main() {
val tream1 = Tream()
val tream2 = Tream()
/**
* 合并
*/
tream1 merge tream2
}
推荐阅读
--END--
识别二维码,关注我们