文章19 | 阅读 10414 | 点赞0
1.引入:运算符重载,最初接触到这个概念是在C++里,Java中是没有的,取而代之的是通过特定接口来实现,比如进行排序比较大小时,我们可以实现Comparable接口。而Kotlin中,又重新支持该特性,因为这样会显得更加直观。
2.如何实现运算符重载:
比如,我们定义一个Point类:
data class Point(val x: Int, val y: Int)
为了方便,这里将其声明为data class(data class的介绍请参看系列中此篇)。现在我们要支持对两个Point对象的使用运算符+实现相加功能,我们可以如何实现呢?
package com.xlh.kotlin.convention
data class Point(val x: Int, val y: Int) {
/**
* 方式一:通过member的形式
* 1. operator 关键字 :运算符重载中必须使用该关键字
* 2. 函数名约定
*/
operator fun plus(o: Point): Point {
return Point(x + o.x, y + o.y)
}
}
fun main(){
val p1 = Point(1, 2)
val p2 = Point(3, 4)
println(p1 + p2) //打印 Point(x=4, y=6)
}
正如我在注释里标注的一样,这里我们只需要注意两点:
operator | function name |
---|---|
+ | plus |
- | minus |
* | times |
/ | div |
% | mod |
然后我们就可以使用+运算符实现两个Point对象的相加。那么运算符重载本质上是怎样的呢,下面我们通过反编译来一探究竟。
来到out/production/JavaSETest(源码在idea下编写)目录下执行:javap -c 全类名(com.xlh.kotlin.convention.PointKt)
可以看得出来,对于此处的p1 + p2,相当于p1.plus(p2),然后会生成一个新的Point对象返回。如果你对于kotlin中的另一个特性——扩展的原理熟悉的话,你会发现这块反编译的结果和它极其相似。所以,我们还可以通过扩展的方式来实现运算符重载。
operator fun Point.minus(other: Point): Point{
return Point(x - other.x, y - other.y)
}
fun main(){
val p1 = Point(1, 2)
val p2 = Point(3, 4)
println(p2 - p1) // 打印Point(x=2, y=2)
}
3.在Java中如何调用:
public static void main(String[] args) {
Point p1 = new Point(10, 20);
Point p2 = new Point(20, 30);
// member
Point plusResult = p1.plus(p2);
// extension
Point minusResult = PointKt.minus(p2, p1);
System.out.println("p1 + p2 = " + plusResult);
System.out.println("p2 - p1 = " + minusResult);
}
4.单向运算符重载不支持交换律.
operator fun Point.times(scale: Double): Point{
return Point((x * scale).toInt(), (y * scale).toInt())
}
对于Point,我们这里重载了*,于是我们可以这样去使用:
val p1 = Point(1, 2)
println(p1 * 2.0) //输出Point(x=2, y=4)
但是,我们不可以写成2.0 * p1,这样编译不会通过。如果要想使用2.0 * p1这种形式,那么我们必须相对地去实现Double.times()
operator fun Double.times(p: Point): Point {
return Point((this * p.x).toInt(), (this * p.y).toInt())
}
5.返回类型可以和参与运算的左右两边类型都不相同。例如:
operator fun Char.times(count: Int): String{
return toString().repeat(count)
}
于是我们可以有:
println('a' * 3) //输出aaa
这里,Char * Int 最终可以得倒一个String类型
6.前面我们所列举的例子都是二元运算符的重载,下面我们来看看一元运算符的重载。
operator | function name |
---|---|
+a | unaryPlus |
-a | unaryMinus |
!a | not |
a, a | inc |
--a, a-- | dec |
我们就以自增运算符为例:
operator fun BigDecimal.inc() = this + BigDecimal.ONE
var n1 = BigDecimal.ONE
println(n1++) //打印1
println(++n1) //打印3
注意:对于需要进行自增或自减的变量必须是var声明的,也很好理解,自增或自减的过程是会修改变量的值的,声明为val自然不合适
7.比较运算符重载
、<、>=、<= : 这类常用用在集合或数组的排序当中,或者是求最值。我们可以定义一个Student类:
class Student(val firstName: String, val lastName: String) : Comparable<Student> {
override fun compareTo(other: Student): Int {
return compareValuesBy(this, other, Student::lastName, Student::firstName)
}
}
于是可以有:
val p1 = Student("san", "zhang")
val p2 = Student("si", "li")
println(p1 > p2) //true
如果你足够细心,你会发现这里的compareTo并没有使用operator关键字修饰,为什么呢?因为在父接口Comparable中,已经使用了operator:
public interface Comparable<in T> {
/**
* Compares this object with the specified object for order. Returns zero if this object is equal
* to the specified [other] object, a negative number if it's less than [other], or a positive number
* if it's greater than [other].
*/
public operator fun compareTo(other: T): Int
}
参考文献:《Kotlin in action》
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/xlh1191860939/article/details/98885253
内容来源于网络,如有侵权,请联系作者删除!