Kotlin学习系列之:运算符重载(operator overloading)

1.引入:运算符重载,最初接触到这个概念是在C++里,Java中是没有的,取而代之的是通过特定接口来实现,比如进行排序比较大小时,我们可以实现Comparable接口。而Kotlin中,又重新支持该特性,因为这样会显得更加直观。

2.如何实现运算符重载:

  • 方式一:在类内定义,以成员方法(member)的形式

    比如,我们定义一个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关键字: 表明该方法用于运算符重载
    • 方法名:方法名是固定的,如果想要重载运算符+,则方法名必须是plus;如果想要重载运算符-,则方法名必须是minus。详情参看下表:

      operatorfunction 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中的另一个特性——扩展的原理熟悉的话,你会发现这块反编译的结果和它极其相似。所以,我们还可以通过扩展的方式来实现运算符重载。

  • 方式二:通过扩展(extension)的方式实现运算符重载

    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.前面我们所列举的例子都是二元运算符的重载,下面我们来看看一元运算符的重载。

operatorfunction name
+aunaryPlus
-aunaryMinus
!anot
++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.比较运算符重载

  • ==:equals()。这个大家应该很熟悉了(结构性相等的比较),我们这里只补充一点,大家有没有想过这样的一个问题,如果左边的值为null,会不会空指针呢?答案是不会的,因为a == b,相当于:a?.equals(b) ?: (b == null)
  • === : 这个不允许被重载(引用性相等的比较)
  • >、<、>=、<= : 这类常用用在集合或数组的排序当中,或者是求最值。我们可以定义一个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》

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值