@[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诒铩!CW懈辍!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()