kotlin学习之--从实现String的‘+’操作到了解操作符重载和函数扩展

@[TOC](kotlin学习之--从实现String的‘+’操作到了解操作符重载和函数扩展)
kotlin学习之--从实现String的‘+’操作到了解操作符重载和函数扩展

前言

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

说了这么多废话,开始进入正题吧,正文如下

正文

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

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

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


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 修饰的函数。

基本操作符列举

一元操作符如下

|表达式| 函数

|--------|-------

| +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代码(点击进入)。

示例对应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()

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,233评论 6 495
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,357评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,831评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,313评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,417评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,470评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,482评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,265评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,708评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,997评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,176评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,503评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,150评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,391评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,034评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,063评论 2 352