![Kotlin从入门到进阶实战](https://wfqqreader-1252317822.image.myqcloud.com/cover/633/26793633/b_26793633.jpg)
2.4 操作符与重载
Kotlin允许我们为自己的类型提供预定义的一组操作符的实现。这些操作符具有固定的符号表示(如“+”或“*”)和固定的优先级。这些操作符的符号定义如下:
![](https://epubservercos.yuewen.com/317ECE/15253386404112206/epubprivate/OEBPS/Images/Figure-P43_41342.jpg?sign=1739588401-q8SFvfNd0yfV0GAA4vW7KhrW2M3md4Fs-0-f7ea6d0998be7bbdd64af542b588458f)
2.4.1 操作符优先级
Kotlin中操作符的优先级(Precedence)如表2-10所示。
表2-10 操作符的优先级
![](https://epubservercos.yuewen.com/317ECE/15253386404112206/epubprivate/OEBPS/Images/Figure-T44_41344.jpg?sign=1739588401-hHS6jXorIiTqVjOJfOiGWZqaxhEOk6N0-0-d1d75c9c9c78825365f5360d9daf40e8)
为实现这些操作符,Kotlin为二元操作符左侧的类型和一元操作符的参数类型提供了相应的函数或扩展函数。重载操作符的函数需要用operator修饰符标记,中缀操作符函数使用infix修饰符标记。
2.4.2 一元操作符
一元操作符(unary operation)有前缀操作符、递增和递减操作符等。
1. 前缀操作符
前缀操作符放在操作数的前面,分别如表2-11所示。
表2-11 前缀操作符
![](https://epubservercos.yuewen.com/317ECE/15253386404112206/epubprivate/OEBPS/Images/Figure-T44_41346.jpg?sign=1739588401-JfqaH800aKyBYeMVIj2xUeyuIgwrMJDi-0-51cf2ffcd3d69b4f568dbe787d2aee84)
以下是重载一元减运算符的示例:
![](https://epubservercos.yuewen.com/317ECE/15253386404112206/epubprivate/OEBPS/Images/Figure-P44_41347.jpg?sign=1739588401-4H2hHct7sRnCADPnRxRkXBd1hUzqI7HB-0-48b8cf59832188855b2bb8e24cb11fa7)
代码说明如下:
第(1)处声明Point数据类。
第(2)处使用operator关键字实现重载函数unaryMinus()。
测试代码如下:
![](https://epubservercos.yuewen.com/317ECE/15253386404112206/epubprivate/OEBPS/Images/Figure-P45_41348.jpg?sign=1739588401-ydYDI8cE0pvg3VwqbbcgqNO5vu167JAD-0-e0233947621b879f83a3459988c75a92)
2. 递增和递减操作符
inc()和dec()函数必须返回一个值,它用于赋值给使用++或--操作的变量。前缀和后缀的表达式的返回值是不同的,具体的取值如表2-12所示。
表2-12 递增和递减操作符
![](https://epubservercos.yuewen.com/317ECE/15253386404112206/epubprivate/OEBPS/Images/Figure-T45_41350.jpg?sign=1739588401-ApWW6muCvuNLJZl7WSA6qzsjFG1yoXU4-0-b55015cace8d2a1001be3e39a398f9ad)
2.4.3 二元操作符
Kotlin中的二元操作符有算术运算符、索引访问操作符、调用操作符、计算并赋值操作符、相等与不等操作符、Elvis操作符、比较操作符、中缀操作符等。下面我们分别介绍。
1. 算术运算符
Kotlin的算术运算符有加、减、乘、除、取余、范围操作符等,如表2-13所示。
表2-13 算术运算符
![](https://epubservercos.yuewen.com/317ECE/15253386404112206/epubprivate/OEBPS/Images/Figure-T45_41352.jpg?sign=1739588401-tMgN6D6Mq2G2Kt9U6frVeSyN5OkFrcFJ-0-f805741a38b46f50f5bb19dec7e81059)
代码示例如下:
![](https://epubservercos.yuewen.com/317ECE/15253386404112206/epubprivate/OEBPS/Images/Figure-P46_41355.jpg?sign=1739588401-eaYVKkOLTwIUT8aig0AGGm1uE6YMPocd-0-2d7954684764b9ae07c57ae1234e396b)
简单的四则运算操作符这里就不多说了。需要注意的是范围运算符a..b与b..a的区别。
2. 字符串的“+”运算符重载
先用代码举个例子:
![](https://epubservercos.yuewen.com/317ECE/15253386404112206/epubprivate/OEBPS/Images/Figure-P46_41356.jpg?sign=1739588401-Hj2vpiEwcBQ4dDpiDXqp3icEZ7NG2gG5-0-de89626d3a66271a66bb76fa84a821eb)
从上面的示例可以看出,在Kotlin中1+""是不允许的(相比Scala语言,写这样的Kotlin代码显得不太友好),只能显式调用toString()函数来相加:
![](https://epubservercos.yuewen.com/317ECE/15253386404112206/epubprivate/OEBPS/Images/Figure-P46_41357.jpg?sign=1739588401-EBxzDVfMzj670z1piQvNECC7rmTqDEFb-0-c3c98a5bed1a3afe65cbf7682503ee1d)
3. 自定义重载的“+”运算符
下面使用一个计数类Counter重载的“+”运算符来增加index的计数值。代码示例如下:
![](https://epubservercos.yuewen.com/317ECE/15253386404112206/epubprivate/OEBPS/Images/Figure-P46_41358.jpg?sign=1739588401-S4DqUukktgLUrRvrSrBH34kMj48JgL41-0-6863034abd6077245cac7c0b6615a956)
测试类如下:
![](https://epubservercos.yuewen.com/317ECE/15253386404112206/epubprivate/OEBPS/Images/Figure-P47_41359.jpg?sign=1739588401-tPqAObLZPZMeLD6P5sd2tR0XeViCMr3r-0-110696d0252f2178f5e5a05f812b17c1)
4. in操作符
in操作符等价于contains()函数,如表2-14所示。
表2-14 in操作符
![](https://epubservercos.yuewen.com/317ECE/15253386404112206/epubprivate/OEBPS/Images/Figure-T47_41361.jpg?sign=1739588401-5UhAFCzgOjbIsrm2oit2g1qZKk144D4u-0-9ee9817a0d427a7d177bfe7370f051d6)
5. 索引访问操作符
索引访问操作符方括号[]转换为调用带有适当数量参数的get和set,如表2-15所示。
表2-15 索引访问操作符
![](https://epubservercos.yuewen.com/317ECE/15253386404112206/epubprivate/OEBPS/Images/Figure-T47_41363.jpg?sign=1739588401-SijKkD3FcY3xuDJ73hOxQo6iTVWpeseg-0-376b82ca7e236953577dcb91fd5c1501)
6. 调用操作符
小括号调用符()转换为调用invoke()函数,同样带参数调用也会转换为invoke()函数中的参数。具体的调用示例如表2-16所示。
表2-16 调用操作符
![](https://epubservercos.yuewen.com/317ECE/15253386404112206/epubprivate/OEBPS/Images/Figure-T47_41365.jpg?sign=1739588401-nwKeo8rVQUAUfvdftrx3R4URlHzUbb1Y-0-317dc402e5c4a7fc660ecba24bca2f0e)
7. 计算并赋值操作符
对于赋值操作,例如a+=b,编译器会试着生成a=a+b的代码(这里包含类型检查:a+b的类型必须是a的子类型)。计算并赋值操作符对应的重载函数如表2-17所示。
表2-17 计算并赋值操作符
![](https://epubservercos.yuewen.com/317ECE/15253386404112206/epubprivate/OEBPS/Images/Figure-T48_41473.jpg?sign=1739588401-8C88JwvkwAd121QB65W4HBAUUvpvhScL-0-ee5ccf562a73d215419cfa04cd773849)
8. 相等与不等操作符
Kotlin中有两种类型的相等性:
引用相等===!==(两个引用指向同一对象);
结构相等==!=(使用equals()判断)。
表2-18 相等与不等操作符
![](https://epubservercos.yuewen.com/317ECE/15253386404112206/epubprivate/OEBPS/Images/Figure-T48_41475.jpg?sign=1739588401-sy0bWhsc3jaYff1zEFhGmCKeWsOxjA0T-0-a9ad4c5724b7b28ca6a7762a8701af62)
“==”操作符有些特殊:它被翻译成一个复杂的表达式,用于筛选null值。意思是:如果a不是null则调用equals(Any?)函数并返回其值;否则(即a ===null)就计算b=== null的值并返回。
当与null显式比较时,a==null会被自动转换为a===null。
注意:===和!==不可重载。
9. Elvis操作符?:
在Kotin中,Elvis操作符特定是跟null进行比较。也就是说
y = x?:0 //使用Elvis 操作符?:
等价于
val y = if(x!==null) x else 0 //等价的if…else逻辑
主要用来作null安全性检查。
Elvis操作符“?:”是一个二元运算符,如果第一个操作数为真,则返回第一个操作数,否则将计算并返回其第二个操作数。它是三元条件运算符的变体,命名灵感来自猫王的发型风格。
Kotlin中没有这样的三元运算符true?1:0,取而代之的是if(true) 1 else 0。而Elvis操作符算是精简版的三元运算符。
我们在Java中使用的三元运算符的语法通常要重复变量两次,示例如下:
String name = "Elvis Presley"; String displayName = (name != null)? name: "Unknown"; //Java中的三元操作符
可以使用Elvis操作符取而代之:
![](https://epubservercos.yuewen.com/317ECE/15253386404112206/epubprivate/OEBPS/Images/Figure-P49_41483.jpg?sign=1739588401-uXLe9X0tWP3Vz3EyCkmtQUWxuqq9FdDb-0-884ca51d64b1fa1da05196f9daac3f88)
可以看出,用Elvis操作符“?:”可以把带有默认值的if…else结构写得极其简短。用Elvis操作符不用检查null(避免了NullPointerException),也不用重复变量。
Elvis操作符的这个功能在Spring表达式语言(SpEL)中有提供,在Kotlin中当然没有理由不支持这个特性了。
代码示例如下:
![](https://epubservercos.yuewen.com/317ECE/15253386404112206/epubprivate/OEBPS/Images/Figure-P49_41484.jpg?sign=1739588401-vejpSTxUpgwTQvW4edZFQRkWU7t85PCY-0-55113a5b8d7ea7e7df6b3da5f9ef11cf)
10. 比较操作符
Kotlin中所有的比较表达式都转换为对compareTo()函数的调用,这个函数需要返回Int值,具体的对应关系如表2-19所示。
表2-19 比较操作符
![](https://epubservercos.yuewen.com/317ECE/15253386404112206/epubprivate/OEBPS/Images/Figure-T49_41487.jpg?sign=1739588401-gupwYIlQzfKZoZYUjuVzcJH4cuAAmGf0-0-66d033965bd0d0fc6ddfb7f7ed728979)
11. 用infix函数自定义中缀操作符
我们可以通过自定义infix函数来实现中缀操作符。代码示例如下:
![](https://epubservercos.yuewen.com/317ECE/15253386404112206/epubprivate/OEBPS/Images/Figure-P49_41485.jpg?sign=1739588401-9zNbY0CCjhZN4oQaQ4nBOeaPvjOJlRbi-0-9318b72ee62bc2ad18b9c13a1c8f4b22)
测试代码如下:
![](https://epubservercos.yuewen.com/317ECE/15253386404112206/epubprivate/OEBPS/Images/Figure-P50_41488.jpg?sign=1739588401-9ZYFMPyrEYackayVjMbI4VioyB4GpIZH-0-63f03b7f9f5d7c2862fb233f9fb6a171)
输出如下:
Person(name=Jack, age=22) Person(name=Jack, age=22)