kotlin 从实现String的‘+’操做到了解操做符重载和函数扩展

前言

         开始工做了,公司大佬建议学学kotlin,可以加快开发速度,能够防止空指针,总之是一堆好处,用起来很爽。but,一开始我是不信的,Java不香吗,kotlin那是啥,据说函数返回类型都在方法名后面,这种奇葩操做的语言。不过,鉴于大佬推荐,貌似了解是有必要的,因而,,,,,,,,,kotlin真香! 说了这么多废话,开始进入正题吧,正文以下html

正文

      众所周知,在Java中,对字符串是有特殊处理的,无论什么对象,哪怕它是个null,字符串都能与其相加。java

      可是当我学习了kotlin,发现不能愉快的用""+的方式愉快的将对象自动转化成String了(虽然kotlin有个更好的字符串模板功能,能更加愉快的写代码,可是,我就是喜欢直接""+,一个杠精程序员),不只仅是String没法相加,不一样类型的数字之间也不能相加了,这让一个Java程序员很难受的事情(可能只是本人难受,哈哈),原本我是要成为kotlin黑粉了,,可是本着找毛病的原则,我继续学习了kotlin,而后,kotlin又香了!!!程序员

当我在kotlin官网学到了扩展函数和操做符函数这一块,我终于明白kotlin为什么如此香了。如何让一个对象可使用‘+’运算符来与String拼接呢?代码以下:shell

operator fun String.plus(i:Int)=this+i.toString()
fun main() {
    val i=5
    val re="2222"+i
    println(re)
}
复制代码

如此,对于Int型变量,咱们就能愉快的直接用 ‘+’ 链接了(plus函数的做用将在后文介绍),不过,要注意一点,此时 i这个Int对象只能位置+的左边,若是在右边,则会报错,以下图所示 设计模式

在这里插入图片描述
这是为啥呢?这里先来科普下kotlin的 扩展函数操做符吧。(点击便可进入官网去看了,官网内容老是最全的!) (ps:空指针检测什么的避免了错误,这固然是kotlin中的 杀手级功能。可是让我以为kotlin香气满满的仍是kotlin中的函数扩展,操做符重载啊,参数可设置默认值这些功能啊) kotlin官方的扩展函数示例和操做符重载已经很清楚了,在此为了文章的完整性,部份内容是来自官方内容。

kotlin 操做符重载

什么是操做符重载?

       Kotlin 容许咱们为本身的类型提供预约义的一组操做符的实现。这些操做符具备固定的符号表示 (如 + 或 *)和固定的优先级。为实现这样的操做符,咱们为相应的类型(即二元操做符左侧的类型和一元操做符的参数类型)提供了一个固定名字的成员函数或扩展函数。 重载操做符的函数须要用operator修饰符标记。 另外,咱们描述为不一样操做符规范操做符重载的约定。 以上就是官方关于运算符的介绍了,对了 一元操做符指代的是如 a++,a-- 这种只对一个表达式执行操做,二元操做符指的是如 a+b,a-b,a*b,a/b 这种将两个表达式合成一个稍复杂的表达式。 在kotlin中,这些操做符其实都是对应一个被operator 修饰的函数。bash

基本操做符列举

一元操做符以下函数

表达式 函数
+a a.unaryPlus()
-a a.unaryMinus()
!a a.not()
a++ a.inc() +
a-- a.dec() +

二元操做符(这里只列举了基本的算术运算符,其余的如in,== 详见官网)源码分析

表达式 函数
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)

示例

从以上咱们能够看出来了,原来操做符对应的就是一个个函数啊。如此,咱们只须要将一个类定义了对应的方法,就能够相加了。这里咱们拿个Money类来举例吧(厚脸拿郭神书中的例子了)学习

class Money(val value: Int) {
    operator fun plus(money: Money): Money {//这是 + 号
        val sum = this.value + money.value
        return Money(sum);
    }

    operator fun plus(money: Int): Money {//这是 + 号
        val sum = this.value + money
        return Money(sum);
    }
}
fun main(){
	val money=Moeny(5)
	val num=5
	val balance=money+num
	println("moeny:$balance")
}
复制代码

此处咱们写了Money类的plus函数,实现了 + 操做符的运算,我想这不须要解释啥了吧,记住就ok,固然,咱们得弄清kotlin如何实现对+运算符的替代的,让咱们来看看其对应的Java代码吧,如何看kotlin对应的Java代码(点击进入)。ui

示例对应Java源码分析

public final class Money {
   private final int value;

   @NotNull
   public final Money plus(@NotNull Money money) {
      Intrinsics.checkParameterIsNotNull(money, "money");
      int sum = this.value + money.value;
      return new Money(sum);
   }

   @NotNull
   public final Money plus(int money) {
      int sum = this.value + money;
      return new Money(sum);
   }

   public final int getValue() {
      return this.value;
   }

   public Money(int value) {
      this.value = value;
   }
}

public final class TempKt {
   private static final StringBuilder build(@NotNull StringBuilder $this$build, Function1 block) {
      block.invoke($this$build);
      return $this$build;
   }

   public static final void main() {
      Money money = new Money(5);
      int num = 5;
      Money balance = money.plus(num);
      String var3 = "moeny:" + balance;
      boolean var4 = false;
      System.out.println(var3);
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
     	 main();
   }
}
复制代码

从代码中咱们能够看出,main函数在java中对应的是一个TempKt类中的java main方法,在入口方法 main中被调用。 Money类没什么好说的,其中方法基本是与kotlin中相对应的,重点在 koltin的val balance=money+num这一行代码,咱们能够看到,在Java中,其被翻译成为这样了Money balance = money.plus(num);,很明显了,+被替换成了对应的方法。不过以上只有两个表达式相加,咱们再来一个看看结果吧.

Money money = new Money(5);
int num1 = 5;
int num2 = 10;
Money balance = money.plus(num1).plus(num2);
复制代码

对应Java代码

Money balance = money.plus(num1).plus(num2);
复制代码

+不断的被替换成了plus

操做符重载小结

就以上代码,咱们了解了kotlin中的操做符重载以及对应的Java代码转换了。同时,我相信也明白了开头那段为啥子String+Int不报错,而Int + String却报错了吧??,由于咱们只实现了一边的plus,咱们实现了左加操做符重载,即String对象在左,Int对象在右。咱们再加上左运算符就ok了,代码以下:

package com.example.jetpacklearn.kotlinlearn
operator fun Int.plus(s:String)=this.toString()+s
operator fun String.plus(i:Int)=this+i.toString()
fun main() {
    val i=5
    val re=i+"2222"
    println(re)
}
复制代码

经过给Int类也加个操做符重载的扩展函数就ok啦。

可是,相信机智的小伙伴们早已发现了,Money类的操做符重载是在其类中定义了方法,显然跟咱们开头实现的String类的操做符重载是不一样的,so,kotlin的又一个特性来了,扩展函数(官方传送门),让咱们接下来探讨下扩展函数(官方传送门)吧。

kotlin 扩展函数

什么是kotlin的扩展函数?

老规矩,官方介绍copy一波       Kotlin 可以扩展一个类的新功能而无需继承该类或者使用像装饰者这样的设计模式。 这经过叫作 扩展 的特殊声明完成。 例如,你能够为一个你不能修改的、来自第三方库中的类编写一个新的函数。 这个新增的函数就像那个原始类原本就有的函数同样,能够用普通的方法调用。 这种机制称为 扩展函数 。此外,也有 扩展属性 , 容许你为一个已经存在的类添加新的属性。

官方介绍很清楚了,扩展函数就是方便开发者可以方便的使用某个方法,而且 该方法绑定在了一个类上,在使用上是更加直观的,你能清楚的知道这个方法是是让谁使用的。还有扩展属性,这点本文不介绍了,你们去官网看下吧,扩展传送门在此

示例

在此仍是使用Money类做为示例吧,Money类中存储了金钱的数值,可是不一样的钱的比例是不一样的,所以咱们须要增长一个货币计算的功能,好比人民币对应的美圆数值。可是咱们并不但愿改变Money类的结构,如此咱们能够用扩展函数,来给Moeny类增长一个名为 convertToDollar的扩展函数

class Money(val value: Double,val type:String){
//省略
}
fun Money.convertToDollar(): Money {
    when(this.type){
        "rmb"->{
            return Money(this.value*6.5,"dollar")
        }
        "dollar"->{return Money(this.value,this.type)}
        else->
            throw Exception("未知类型")
    }
}
fun main(){
    val rmb=Money(5.0)
    println("moeny value:${rmb.value} type:${rmb.type}")
    val dollar=rmb.convertToDollar()
    println("moeny value:${dollar.value} type:${dollar.type}")
    
}
复制代码
# 运行结果
moeny   value:5.0  type:rmb
moeny   value:0.7692307692307693  type:dollar
复制代码

示例生成的Java代码

在示例中,咱们实现了扩展函数的编写,而且运行了,那么这一段kotlin代码对应的Java代码是怎么样的呢?反编译的Java代码以下:

public final class TestKt {
   private static final StringBuilder build(@NotNull StringBuilder $this$build, Function1 block) {
      block.invoke($this$build);
      return $this$build;
   }

   @NotNull
   public static final Money convertToDollar(@NotNull Money $this$convertToDollar) {
      Intrinsics.checkParameterIsNotNull($this$convertToDollar, "$this$convertToDollar");
      String var1 = $this$convertToDollar.getType();
      switch(var1.hashCode()) {
      case -1326217028:
         if (var1.equals("dollar")) {
            return new Money($this$convertToDollar.getValue(), $this$convertToDollar.getType());
         }
         break;
      case 113031:
         if (var1.equals("rmb")) {
            return new Money($this$convertToDollar.getValue() / 6.5D, "dollar");
         }
      }

      throw (Throwable)(new Exception("未知类型"));
   }

   public static final void main() {
      Money rmb = new Money(5.0D, (String)null, 2, (DefaultConstructorMarker)null);
      String var1 = "moeny value:" + rmb.getValue() + " type:" + rmb.getType();
      boolean var2 = false;
      System.out.println(var1);
      Money dollar = convertToDollar(rmb);
      String var5 = "moeny value:" + dollar.getValue() + " type:" + dollar.getType();
      boolean var3 = false;
      System.out.println(var5);
   }

   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }
}
复制代码

从以上代码,咱们能够发现,扩展函数对应的java中的方法并非出如今Money类中,其在TestKt这个类中,也就是kotlin main函数所在类中。在convertToDollar这个方法中,传入了一个Money变量,经过这个$this$convertToDollar变量value,type值(有的小伙伴可能会对这个变量产生奇怪,会以为这是一个特殊的变量,可是实际上,这就是一个再普通不过的一个变量名,以$做为了开始符,这个this也不是真的是对象内部的this,这是个普通的字符,更加形象花的一种命名方式罢了)。同时,咱们经过该段代码也发现了when这个kotlin中的关键字是如何转化了Java中的switch。 好了,到此为止,扩展函数也讲完了。 再次回到咱们的开头,String 的 ‘+’操做符的实现,发现如此简单,哈哈。

kotlin 泛型

不过!!!!!!!!!!!!!!!! 你会发现,这只是实现了 Int对象与String对象的 ‘+’操做,难道每一个类咱们都要重写一遍代码,才能实现其与String对象的‘+’操做????,那也太累了吧!!!!!!!!!!! 哈哈,这里固然是有解决方法的了,那就是,泛型了,与java同样,kotlin也是支持泛型的,相信Java的泛型你们都很熟悉了,kotlin的泛型相比Java的泛型使用也没有太大区别。让咱们用泛型让 java 中 字符串相加操做真正移植到kotlin中来吧!!!代码以下:

operator fun <T> T.plus(s: String) = this.toString() + s
operator fun String.plus(t:Any)= this+t.toString()
复制代码

小结

本文从String ‘+’操做符的实现,扩展讲述了kotlin的操做符重载,函数扩展等知识,刚开始写文章,若有问题,还请批评指正

原创声明

做者:陈浩亮

连接:

  1. csdn kotlin 从实现String的‘+’操做到了解操做符重载和函数扩展
  2. 简书 kotlin 从实现String的‘+’操做到了解操做符重载和函数扩展
相关文章
相关标签/搜索