kotlin學(xué)習(xí)之--從實(shí)現(xiàn)String的‘+’操作到了解操作符重載和函數(shù)擴(kuò)展

@[TOC](kotlin學(xué)習(xí)之--從實(shí)現(xiàn)String的‘+’操作到了解操作符重載和函數(shù)擴(kuò)展)
kotlin學(xué)習(xí)之--從實(shí)現(xiàn)String的‘+’操作到了解操作符重載和函數(shù)擴(kuò)展

前言

?????????開始工作了准谚,公司大佬建議學(xué)學(xué)kotlin柱衔,能夠加快開發(fā)速度唆铐,可以防止空指針奔滑,總之是一堆好處,用起來(lái)很爽澳盐。but,一開始我是不信的,Java不香嗎腕窥,kotlin那是啥,聽說(shuō)函數(shù)返回類型都在方法名后面癞松,這種奇葩操作的語(yǔ)言响蓉。不過(guò)哨毁,鑒于大佬推薦,貌似了解是有必要的粱栖,于是闹究,,渣淤,价认,率挣,椒功,,丁屎,晨川,kotlin真香删豺!

說(shuō)了這么多廢話呀页,開始進(jìn)入正題吧,正文如下

正文

??????眾所周期尘分,在Java中培愁,對(duì)字符串是有特殊處理的定续,不管什么對(duì)象,哪怕它是個(gè)null,字符串都能與其相加香罐。

??????但是 當(dāng)我學(xué)習(xí)了kotlin庇茫,發(fā)現(xiàn)不能愉快的用""+的方式愉快的將對(duì)象自動(dòng)轉(zhuǎn)化成String了(雖然kotlin有個(gè)更好的字符串模板功能,能更加愉快的寫代碼查坪,但是偿曙,我就是喜歡直接""+,一個(gè)杠精程序員)望忆,不僅僅是String無(wú)法相加,不同類型的數(shù)字之間也不能相加了启摄,這讓一個(gè)Java程序員很難受的事情(可能只是本人難受歉备,哈哈)蕾羊,本來(lái)我是要成為kotlin黑粉了,龟再,但是本著找毛病的原則吸申,我繼續(xù)學(xué)習(xí)了kotlin享甸,然后蛉威,kotlin又香了r窍印!束凑!

當(dāng)我在kotlin官網(wǎng)學(xué)到了擴(kuò)展函數(shù)和操作符函數(shù)這一塊栅盲,我終于明白kotlin為何如此香了谈秫。如何讓一個(gè)對(duì)象可以使用 ‘+’運(yùn)算符來(lái)與String拼接呢?代碼如下:


operator fun String.plus(i:Int)=this+i.toString()

fun main() {

    val i=5

    val re="2222"+i

    println(re)

}

如此该编,對(duì)于Int型變量课竣,我們就能愉快的直接用 ‘+’ 連接了(plus函數(shù)的作用將在后文介紹)置媳,不過(guò)半开,要注意一點(diǎn),此時(shí) i這個(gè)Int對(duì)象只能位置+的左邊奢米,如果在右邊纠永,則會(huì)報(bào)錯(cuò),如下圖所示

在這里插入圖片描述

這是為啥呢涉波?這里先來(lái)科普下kotlin的擴(kuò)展函數(shù)操作符吧炭序。(點(diǎn)擊即可進(jìn)入官網(wǎng)去看了,官網(wǎng)內(nèi)容總是最全的窗声!)

(ps:空指針檢測(cè)什么的避免了錯(cuò)誤笨觅,這當(dāng)然是kotlin中的殺手級(jí)功能。但是讓我覺得kotlin香氣滿滿的還是kotlin中的函數(shù)擴(kuò)展杀糯,操作符重載啊苍苞,參數(shù)可設(shè)置默認(rèn)值這些功能捌馄 )

kotlin官方的擴(kuò)展函數(shù)示例和操作符重載已經(jīng)很清楚了,在此為了文章的完整性方援,部分內(nèi)容是來(lái)自官方內(nèi)容犯戏,跪求不噴

kotlin 操作符重載

什么是操作符重載拳话?

???????Kotlin 允許我們?yōu)樽约旱念愋吞峁╊A(yù)定義的一組操作符的實(shí)現(xiàn)。這些操作符具有固定的符號(hào)表示 (如 + 或 )和固定的優(yōu)先級(jí)呀非。為實(shí)現(xiàn)這樣的操作符镜盯,我們?yōu)橄鄳?yīng)的類型(即二元操作符左側(cè)的類型和一元操作符的參數(shù)類型)提供了一個(gè)固定名字的成員函數(shù)或擴(kuò)展函數(shù)。 重載操作符的函數(shù)需要用operator*修飾符標(biāo)記降允。

另外剧董,我們描述為不同操作符規(guī)范操作符重載的約定翅楼。

以上就是官方關(guān)于運(yùn)算符的介紹了真慢,對(duì)了 一元操作符指代的是如 a++,a-- 這種只對(duì)一個(gè)表達(dá)式執(zhí)行操作晤碘,二元操作符指的是如 a+b园爷,a-b,a*b求厕,a/b 這種將兩個(gè)表達(dá)式合成一個(gè)稍復(fù)雜的表達(dá)式呀癣。

在kotlin中弦赖,這些操作符其實(shí)都是對(duì)應(yīng)一個(gè)被operator 修飾的函數(shù)蹬竖。

基本操作符列舉

一元操作符如下

|表達(dá)式| 函數(shù)

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

| +a |a.unaryPlus()|

|-a |a.unaryMinus()

|!a |a.not()

|a++ |a.inc() +

|a-- |a.dec() +

二元操作符(這里只列舉了基本的算術(shù)運(yùn)算符币厕,其他的如in,== 詳見官網(wǎng))

|表達(dá)式| 函數(shù)

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

|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)

示例

從以上我們可以看出來(lái)了,原來(lái)操作符對(duì)應(yīng)的就是一個(gè)個(gè)函數(shù)啊店乐。如此响巢,我們只需要將一個(gè)類定義了對(duì)應(yīng)的方法踪古,就可以相加了伏穆。這里我們拿個(gè)Money類來(lái)舉例吧(厚臉拿郭神書中的例子了)


class Money(val value: Int) {

    operator fun plus(money: Money): Money {//這是 + 號(hào)

        val sum = this.value + money.value

        return Money(sum);

    }

    operator fun plus(money: Int): Money {//這是 + 號(hào)

        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函數(shù)纷纫,實(shí)現(xiàn)了 + 操作符的運(yùn)算辱魁,我想這不需要解釋啥了吧,記住就ok,當(dāng)然参滴,我們得弄清kotlin如何實(shí)現(xiàn)對(duì)+運(yùn)算符的替代的蝌箍,讓我們來(lái)看看其對(duì)應(yīng)的Java代碼吧妓盲,如何看kotlin對(duì)應(yīng)的Java代碼(點(diǎn)擊進(jìn)入)悯衬。

示例對(duì)應(yīng)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函數(shù)在java中對(duì)應(yīng)的是一個(gè)TempKt類中的java main方法击胜,在入口方法 main中被調(diào)用偶摔。

Money類沒(méi)什么好說(shuō)的辰斋,其中方法基本是與kotlin中相對(duì)應(yīng)的宫仗,重點(diǎn)在 koltin的val balance=money+num這一行代碼藕夫,我們可以看到毅贮,在Java中滩褥,其被翻譯成為這樣了Money balance = money.plus(num);,很明顯了俗孝,+被替換成了對(duì)應(yīng)的方法魄健。不過(guò)以上只有兩個(gè)表達(dá)式相加,我們?cè)賮?lái)一個(gè)看看結(jié)果吧.


Money money = new Money(5);

int num1 = 5;

int num2 = 10;

Money balance = money.plus(num1).plus(num2);

對(duì)應(yīng)Java代碼


Money balance = money.plus(num1).plus(num2);

+不斷的被替換成了plus诀艰。

操作符重載小結(jié)

就以上代碼,我們了解了kotlin中的操作符重載以及對(duì)應(yīng)的Java代碼轉(zhuǎn)換了饮六。同時(shí)其垄,我相信也明白了開頭那段為啥子String+Int不報(bào)錯(cuò),而Int + String卻報(bào)錯(cuò)了吧卤橄?绿满?,因?yàn)槲覀冎粚?shí)現(xiàn)了一邊的plus窟扑,我們實(shí)現(xiàn)了左加操作符重載喇颁,即String對(duì)象在左,Int對(duì)象在右嚎货。我們?cè)偌由献筮\(yùn)算符就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)

}

通過(guò)給Int類也加個(gè)操作符重載的擴(kuò)展函數(shù)就ok啦。

但是外潜,相信機(jī)智的小伙伴們?cè)缫寻l(fā)現(xiàn)了,Money類的操作符重載是在其類中定義了方法,顯然跟我們開頭實(shí)現(xiàn)的String類的操作符重載是不同的嵌灰,so,kotlin的又一個(gè)特性來(lái)了,擴(kuò)展函數(shù)(官方傳送門)城丧,讓我們接下來(lái)探討下擴(kuò)展函數(shù)(官方傳送門)吧。

kotlin 擴(kuò)展函數(shù)

什么是kotlin的擴(kuò)展函數(shù)蚊惯?

老規(guī)矩,官方介紹copy一波

??????Kotlin 能夠擴(kuò)展一個(gè)類的新功能而無(wú)需繼承該類或者使用像裝飾者這樣的設(shè)計(jì)模式。 這通過(guò)叫做 擴(kuò)展 的特殊聲明完成波闹。 例如,你可以為一個(gè)你不能修改的、來(lái)自第三方庫(kù)中的類編寫一個(gè)新的函數(shù)滋捶。 這個(gè)新增的函數(shù)就像那個(gè)原始類本來(lái)就有的函數(shù)一樣惧财,可以用普通的方法調(diào)用厅翔。 這種機(jī)制稱為 擴(kuò)展函數(shù) 。此外顽分,也有 擴(kuò)展屬性 , 允許你為一個(gè)已經(jīng)存在的類添加新的屬性缸沃。

官方介紹很清楚了,擴(kuò)展函數(shù)就是方便開發(fā)者能夠方便的使用某個(gè)方法仇箱,并且 該方法綁定在了一個(gè)類上,在使用上是更加直觀的,你能清楚的知道這個(gè)方法是是讓誰(shuí)使用的斟薇。還有擴(kuò)展屬性,這點(diǎn)本文不介紹了,大家去官網(wǎng)看下吧发笔,擴(kuò)展傳送門在此

示例

在此還是使用Money類作為示例吧,Money類中存儲(chǔ)了金錢的數(shù)值韭赘,但是不同的錢的比例是不同的苞冯,因此我們需要增加一個(gè)貨幣計(jì)算的功能,比如人民幣對(duì)應(yīng)的美元數(shù)值。但是我們并不希望改變Money類的結(jié)構(gòu),如此我們可以用擴(kuò)展函數(shù)糊闽,來(lái)給Moeny類增加一個(gè)名為 convertToDollar的擴(kuò)展函數(shù)


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}")



}


# 運(yùn)行結(jié)果

moeny  value:5.0  type:rmb

moeny  value:0.7692307692307693  type:dollar

示例生成的Java代碼

在示例中傀履,我們實(shí)現(xiàn)了擴(kuò)展函數(shù)的編寫絮宁,并且運(yùn)行了,那么這一段kotlin代碼對(duì)應(yīng)的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();

  }

}

從以上代碼斗塘,我們可以發(fā)現(xiàn)茧吊,擴(kuò)展函數(shù)對(duì)應(yīng)的java中的方法并不是出現(xiàn)在Money類中休讳,其在TestKt這個(gè)類中雏婶,也就是kotlin main函數(shù)所在類中错维。在convertToDollar這個(gè)方法中仰楚,傳入了一個(gè)Money變量侨嘀,通過(guò)這個(gè)$this$convertToDollar變量value郎汪,type值(有的小伙伴可能會(huì)對(duì)這個(gè)變量產(chǎn)生奇怪照筑,會(huì)覺得這是一個(gè)特殊的變量懦铺,但是實(shí)際上,這就是一個(gè)再普通不過(guò)的一個(gè)變量名,以$作為了開始符,這個(gè)this也不是真的是對(duì)象內(nèi)部的this,這是個(gè)普通的字符,更加形象花的一種命名方式罷了)另假。同時(shí)戈轿,我們通過(guò)該段代碼也發(fā)現(xiàn)了when這個(gè)kotlin中的關(guān)鍵字是如何轉(zhuǎn)化了Java中的switch誊册。

好了,到此為止局蚀,擴(kuò)展函數(shù)也講完了料祠。

再次回到我們的開頭,String 的 ‘+’操作符的實(shí)現(xiàn),發(fā)現(xiàn)如此簡(jiǎn)單摆碉,哈哈驰徊。

kotlin 泛型

不過(guò)N!!E杈浴F尬丁3狻5浞睢!4志恪>牡稹!!G思础H锩纭G迫帷撼唾!

你會(huì)發(fā)現(xiàn),這只是實(shí)現(xiàn)了 Int對(duì)象與String對(duì)象的 ‘+’操作,難道每個(gè)類我們都要重寫一遍代碼塌西,才能實(shí)現(xiàn)其與String對(duì)象的‘+’操作殊霞?蜒什??疤估?灾常,那也太累了吧!A迥础8诒铩!CW懈辍!E±取<嗯恰!吧碾!

哈哈凰盔,這里當(dāng)然是有解決方法的了,那就是倦春,泛型了户敬,與java一樣,kotlin也是支持泛型的睁本,相信Java的泛型大家都很熟悉了尿庐,kotlin的泛型相比Java的泛型使用也沒(méi)有太大區(qū)別。讓我們用泛型讓 java 中 字符串相加操作真正移植到kotlin中來(lái)吧D匮摺3!代碼如下:


operator fun <T> T.plus(s: String) = this.toString() + s

operator fun String.plus(t:Any)= this+t.toString()

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末枉疼,一起剝皮案震驚了整個(gè)濱河市皮假,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌骂维,老刑警劉巖惹资,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異航闺,居然都是意外死亡褪测,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)汰扭,“玉大人,你說(shuō)我怎么就攤上這事福铅÷苊” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵滑黔,是天一觀的道長(zhǎng)笆包。 經(jīng)常有香客問(wèn)我,道長(zhǎng)略荡,這世上最難降的妖魔是什么庵佣? 我笑而不...
    開封第一講書人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮汛兜,結(jié)果婚禮上巴粪,老公的妹妹穿的比我還像新娘。我一直安慰自己粥谬,他們只是感情好肛根,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著漏策,像睡著了一般派哲。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上掺喻,一...
    開封第一講書人閱讀 49,772評(píng)論 1 290
  • 那天芭届,我揣著相機(jī)與錄音,去河邊找鬼感耙。 笑死褂乍,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的即硼。 我是一名探鬼主播树叽,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼谦絮!你這毒婦竟也來(lái)了题诵?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤层皱,失蹤者是張志新(化名)和其女友劉穎性锭,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叫胖,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡草冈,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片怎棱。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡哩俭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拳恋,到底是詐尸還是另有隱情凡资,我是刑警寧澤,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布谬运,位于F島的核電站隙赁,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏梆暖。R本人自食惡果不足惜伞访,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望轰驳。 院中可真熱鬧厚掷,春花似錦、人聲如沸级解。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蠕趁。三九已至薛闪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間俺陋,已是汗流浹背豁延。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留腊状,地道東北人诱咏。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像缴挖,于是被迫代替她去往敵國(guó)和親袋狞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348