Kotlin擴(kuò)展函數(shù)與運(yùn)算符重載超詳細(xì)解析
一、擴(kuò)展函數(shù)
不少現(xiàn)代高級(jí)編程語(yǔ)言中有擴(kuò)展函數(shù)這個(gè)概念,Java卻一直以來(lái)都不支持這個(gè)功能,Kotlin對(duì)擴(kuò)展函數(shù)有了很好的支持。
擴(kuò)展函數(shù)表示即使在不修改某個(gè)類的源碼的情況下,仍然可以打開這個(gè)類,向該類添加新的函數(shù)。
比如有一個(gè)功能:一段字符串中可能包含字母、數(shù)字和特殊符號(hào)等字符,現(xiàn)在我們希望統(tǒng)計(jì)字符串中字母的數(shù)量,要怎么實(shí)現(xiàn)這個(gè)功能?如果按照一般的編程思維,可能會(huì)很自然的寫出如下函數(shù):
object StringUtil{ fun lettersCount(str:String):Int{ var count=0 for(char in str){ if(char.isLetter()){ count++ } } return count } }
這里先定義了一個(gè)StringUtil單例類,然后在這個(gè)單例類中定義一個(gè)lettersCount()函數(shù),該函數(shù)接收一個(gè)字符串參數(shù)。在lettersCount()方法中,我們使用for-in循環(huán)去遍歷字符串中的每一個(gè)字符。如果該字符是一個(gè)字母的話,那么計(jì)數(shù)器加1,最終返回計(jì)數(shù)器的值。
現(xiàn)在我們需要統(tǒng)計(jì)某個(gè)字符串中的字母數(shù)量時(shí),只需要編寫如下代碼:
val str="ABCdsw1242" val Count = StringUtil.lettersCount(str)
這種寫法可以正常工作,這也是Java編程中的標(biāo)準(zhǔn)實(shí)現(xiàn)思維,但有了擴(kuò)展函數(shù)之后就不一樣了,我們可以使用一種更加面向思維來(lái)實(shí)現(xiàn)這個(gè)功能,比如說(shuō)lettersCount()函數(shù)添加到String類當(dāng)中。
我們先來(lái)學(xué)習(xí)一下定義擴(kuò)展函數(shù)的語(yǔ)法結(jié)構(gòu),如下所示:
fun ClassName.methodName(param1:Int,param2:Int):Int{ return 0 }
想定義普通的函數(shù),定義擴(kuò)展函數(shù)只需要在函數(shù)名的前面加上一個(gè)ClassName.的語(yǔ)法結(jié)構(gòu),就表示將該函數(shù)添加到指定類當(dāng)中。
接下來(lái)使用擴(kuò)展函數(shù)的方式來(lái)優(yōu)化剛才的統(tǒng)計(jì)功能。
由于我們希望向String類中添加一個(gè)擴(kuò)展函數(shù),因此需要先創(chuàng)建一個(gè)String.kt文件。文件名雖然沒(méi)有固定的要求,但是我建議向哪個(gè)類中添加擴(kuò)展函數(shù),就定義一個(gè)同名的Kotlin文件,這樣便于你以后查找。當(dāng)前,擴(kuò)展函數(shù)也是可以定義在任何一個(gè)現(xiàn)有類當(dāng)中的,并不一定非要?jiǎng)?chuàng)建新文件。不過(guò)通常來(lái)說(shuō),最好將它定義成頂層方法,這樣可以讓擴(kuò)展函數(shù)擁有全局的訪問(wèn)域。
現(xiàn)在在String.kt文件中編寫如下代碼:
fun String.lettersCount():Int{ var count=0 for(char in this){ if(char.isLetter()){ count++ } } return count }
注意這里的代碼變化,現(xiàn)在我們將lettersCount()方法定義成了String類的擴(kuò)展函數(shù),那么函數(shù)中就自動(dòng)擁有了String實(shí)例的上下文。因此lettersCount()函數(shù)就不再需要接收一個(gè)字符串參數(shù)了,而是直接遍歷this即可,因?yàn)楝F(xiàn)在this就代表著字符串本身。
定義好了擴(kuò)展函數(shù)之后,統(tǒng)計(jì)某個(gè)字符串中的字母數(shù)量只需要這樣寫即可:
val count="ABCdsw1242".lettersCount()
這樣看上去是String類中自帶了lettersCount()方法一樣。
擴(kuò)展函數(shù)在很多情況下可以讓API變得更加簡(jiǎn)潔、豐富,更加面向?qū)ο?。我們?cè)俅我許tring類為例,這是一個(gè)final類,任何一個(gè)類都不可以繼承它,也就是說(shuō)它的API只有固定的那些而已,至少在Java中就是如此。然而到了Kotlin中就不一樣了,我們可以向Kotlin類中擴(kuò)展任何函數(shù),使他的API變得更加豐富。比如,你會(huì)發(fā)現(xiàn)Kotlin中的String甚至還有reverse()函數(shù)用于反轉(zhuǎn)字符串,capitalize()函數(shù)用于對(duì)首字母進(jìn)行大寫,等等,這都是Kotlin語(yǔ)言自帶的一些擴(kuò)展函數(shù)。
二、運(yùn)算符重載
在Java中有許多語(yǔ)言內(nèi)置的運(yùn)算符關(guān)鍵字,如+,-,*,/,%,++,–。而Kotlin允許我們將所有的運(yùn)算符甚至其他的關(guān)鍵字進(jìn)行重載,從而拓展這些運(yùn)算符和關(guān)鍵字的用法。
我們基本上都使用過(guò)加減乘除這種四則運(yùn)算符,在編程語(yǔ)言里面,兩個(gè)數(shù)字相加表示求這兩個(gè)數(shù)字之和,兩個(gè)字符串相加表示對(duì)這兩個(gè)字符串進(jìn)行拼接。但是在Kotlin語(yǔ)言中,kotlin運(yùn)算符重載卻允許我們讓任意兩個(gè)對(duì)象相加,或者進(jìn)行更多其他的運(yùn)算操作。
運(yùn)算符重載使用的是operator關(guān)鍵字,只要在指定函數(shù)的前面加上operator關(guān)鍵字,就可以實(shí)現(xiàn)運(yùn)算符重載的功能了。但問(wèn)題是在于這個(gè)指定函數(shù)是什么?這是運(yùn)算符重載里面比較復(fù)雜的一個(gè)問(wèn)題,因?yàn)椴煌倪\(yùn)算符對(duì)應(yīng)的重載函數(shù)也是不同的。比如說(shuō)加號(hào)運(yùn)算符對(duì)應(yīng)的是plus()函數(shù),減號(hào)運(yùn)算符對(duì)應(yīng)的是minus()函數(shù)。
我們這里還是以加號(hào)運(yùn)算符為例,如果想要實(shí)現(xiàn)讓兩個(gè)對(duì)象相加的功能,那么它的語(yǔ)法結(jié)構(gòu)如下:
class obj{ operator fun plus(obj:Obj):Obj{ //處理相加的邏輯 } }
在上述的語(yǔ)法結(jié)構(gòu)中,關(guān)鍵字operator和函數(shù)名plus都是固定不變的,而接收的參數(shù)和函數(shù)返回值可以根據(jù)你的邏輯自行設(shè)定。那么上述代碼就表示一個(gè)Obj對(duì)象可以與另一個(gè)Obj對(duì)象相加,最終返回一個(gè)新的Obj對(duì)象。對(duì)應(yīng)的調(diào)用方式如下:
val obj1=Obj() val obj2=Obj() val obj3=obj1+obj2
這種obj1+obj2的語(yǔ)法其實(shí)就是Kotlin給我們提供的一種語(yǔ)法糖,它會(huì)在編譯的時(shí)候被轉(zhuǎn)換成obj1.plus(obj2)的調(diào)用方式。
舉一個(gè)例子實(shí)現(xiàn)讓兩個(gè)Money對(duì)象相加
首先定義Money類的結(jié)構(gòu),這里讓Money的主構(gòu)造函數(shù)接收一個(gè)value參數(shù),用于表示錢的金額。創(chuàng)建Money.kt文件,代碼如下所示:
class Money(val value:Int)
定義好了Money類的結(jié)構(gòu),接下來(lái)我們就使用運(yùn)算符來(lái)重載實(shí)現(xiàn)讓兩個(gè)Money對(duì)象相加的功能:
class Money(val value:Int) { operator fun plus(money: Money):Money{ val sum=value+money.value return Money(sum) } }
可以看到,這里使用了operator關(guān)鍵字來(lái)修飾plus()函數(shù),這是必不可少的。在plus()函數(shù)中,我們將當(dāng)前Money對(duì)象的value和參數(shù)傳入的Money對(duì)象的value相加,然后將得到的和傳入給一個(gè)新的Money對(duì)象并將該對(duì)象返回。這樣兩個(gè)Money對(duì)象就可以相加了。
我們可以使用如下代碼進(jìn)行測(cè)試
val money1 = Money(5) val money2 = Money(10) val money3=money1+money2 println(money3.value)
最終結(jié)果為15
Money對(duì)象能夠直接和數(shù)字相加,因?yàn)镵otlin允許我們對(duì)同一個(gè)運(yùn)算符進(jìn)行多重重載,代碼如下:
class Money(val value:Int) { operator fun plus(money: Money):Money{ val sum=value+money.value return Money(sum) } //對(duì)同一個(gè)運(yùn)算符進(jìn)行多重重載 operator fun plus(newValue:Int):Money{ val sum=value+newValue return Money(sum) } }
這里我們又重載了一個(gè)plus()函數(shù),不過(guò)這次接收的參數(shù)是一個(gè)整型數(shù)字,其他代碼基本一樣。
那么現(xiàn)在Money對(duì)象就擁有了和數(shù)字相加的能力:
val money1 = Money(5) val money2 = Money(10) val money3=money1+money2 val money4=money3+20 println(money4.value)
打印結(jié)果為35
Kotlin允許我們重載的運(yùn)算符和關(guān)鍵字多達(dá)十幾個(gè)。表中列出了所有可能常用的可重載運(yùn)算符和關(guān)鍵字對(duì)應(yīng)的語(yǔ)法糖表達(dá)式,以及它們會(huì)被轉(zhuǎn)換成的實(shí)際調(diào)用函數(shù)。如果想重載其中的某一種運(yùn)算符或關(guān)鍵字,只要參考剛才加號(hào)運(yùn)算符重載的寫法去實(shí)現(xiàn)就行了。
語(yǔ)法糖表達(dá)式 | 實(shí)際調(diào)用函數(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++ | a.inc() |
a– | a.dec() |
+a | a.unaryPlus |
-a | a.unaryMinus |
!a | a.not |
a==b | a.equals(b) |
a>=b | a.compareTo(b) |
a…b | a.rangeTo(b) |
a[b] | a.get(b) |
a[b]=c | a.set(b,c) |
a in b | b.contains(a) |
注意,最后一個(gè)a in b的語(yǔ)法糖表達(dá)式對(duì)應(yīng)的實(shí)際調(diào)用函數(shù)是b.contains(a),a、b對(duì)象順序是反過(guò)來(lái)的。因?yàn)閍 in b表示判斷a是否在b當(dāng)中,而b.contains(a)表示b是否包含a,因此這兩種表達(dá)式是等價(jià)的。
舉個(gè)例子,Kotlin中的String類就對(duì)contains()函數(shù)進(jìn)行了重載,因此當(dāng)我們判斷“hello”字符串中是否包含“he”子串時(shí),首先可以這樣寫:
if("hello".contains("he")){ }
而借助重載的語(yǔ)法糖表達(dá)式,也可以這樣寫:
if("he" in "hello"){ }
結(jié)合學(xué)習(xí)的擴(kuò)展函數(shù)以及運(yùn)算符重載的知識(shí),對(duì)以下功能進(jìn)行優(yōu)化
fun getRandomLengthString(str:String):String{ val n=(1..20).random() val builder=StringBuilder() repeat(n){ builder.append(str) } return builder.toString() }
其實(shí)這個(gè)函數(shù)的核心思想就是將傳入的字符串重復(fù)n次,而Kotlin能夠讓我們使用str*n這種寫法來(lái)表示讓str字符串重復(fù)n次,使語(yǔ)法更精簡(jiǎn)。
要讓一個(gè)字符串可以乘以一個(gè)數(shù)字,那么肯定要在String類中重載乘號(hào)運(yùn)算符才行,但是String類是系統(tǒng)提供的類,我們無(wú)法修改這個(gè)類的代碼。這個(gè)時(shí)候就可以借助擴(kuò)展函數(shù)功能向String類中添加新函數(shù)了。
既然是向String類中添加擴(kuò)展函數(shù),那么我們還是打開剛才創(chuàng)建的String.kt文件,然后加入如下代碼:
operator fun String.times(n:Int):String{ val builder=StringBuilder() repeat(n){ builder.append(this) } return builder.toString() }
首先,operator關(guān)鍵字肯定是必不可少的,然后是要重載乘號(hào)運(yùn)算符,參考上面的表,如果要乘那么函數(shù)名必須是times,最后由于是定義擴(kuò)展函數(shù),因此還要在方法名前面加上String.的語(yǔ)法結(jié)構(gòu)。在times()函數(shù)中,我們借助StringBuilder和repeat函數(shù)將字符串重復(fù)n次,最終將結(jié)果返回。
現(xiàn)在,字符串就有了和一個(gè)數(shù)字相乘的能力,比如執(zhí)行如下代碼:
val str="abc"*3 println(str)
最終打印結(jié)果是:abcabcabc。
現(xiàn)在我們就可以在getRandomLengthString()函數(shù)中使用這種寫法了
operator fun String.times(n:Int)=str*(1..20).random()
另外,必須說(shuō)明的是,其實(shí)Kotlin的String類中已經(jīng)提供了一個(gè)用于將字符串重復(fù)n遍的repeat()函數(shù),因此times()函數(shù)還可以進(jìn)一步精簡(jiǎn)成如下形式:
operator fun String.times(n:Int)=repeat(n)
到此這篇關(guān)于Kotlin擴(kuò)展函數(shù)與運(yùn)算符重載超詳細(xì)解析的文章就介紹到這了,更多相關(guān)Kotlin擴(kuò)展函數(shù)與運(yùn)算符重載內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Flutter實(shí)現(xiàn)底部導(dǎo)航欄效果
這篇文章主要為大家詳細(xì)介紹了Flutter實(shí)現(xiàn)底部導(dǎo)航欄效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-08-08Android編程實(shí)現(xiàn)圖標(biāo)拖動(dòng)效果的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)圖標(biāo)拖動(dòng)效果的方法,涉及Android事件響應(yīng)及圖標(biāo)變換的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11Andriod Service與Thread的區(qū)別介紹
我們要明確Service是運(yùn)行在主線程的,不能有耗時(shí)操作,這樣,在Service中處理耗時(shí)操作的時(shí)候,我們依然需要使用線程來(lái)處理,既然在Service里也要?jiǎng)?chuàng)建一個(gè)子線程,那為什么不直接在Activity里創(chuàng)建呢,下面通過(guò)本文給大家介紹Andriod Service與Thread的區(qū)別,一起看看吧2017-04-04詳解React Native監(jiān)聽Android回退按鍵與程序化退出應(yīng)用
這篇文章主要介紹了詳解React Native監(jiān)聽Android回退按鍵與程序化退出應(yīng)用的相關(guān)資料,希望通過(guò)本文能幫助到大家,需要的朋友可以參考下2017-09-09Flutter?fluro時(shí)報(bào)錯(cuò)type?'String'?is?not?a?subty
這篇文章主要介紹了Flutter使用fluro時(shí)報(bào)錯(cuò)type?'String'?is?not?a?subtype?of?type?'Queue<Task>'解決方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助2023-12-12Android Studio Gradle插件版本與Gradle版本之間的對(duì)應(yīng)關(guān)系
今天小編就為大家分享一篇關(guān)于Android Studio Gradle插件版本與Gradle版本之間的對(duì)應(yīng)關(guān)系,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12Android利用Java優(yōu)雅消除復(fù)雜條件表達(dá)式的方法
這篇文章主要介紹了Android利用Java優(yōu)雅消除復(fù)雜條件表達(dá)式,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值。感興趣的小伙伴可以參考一下2022-06-06Android開發(fā)使用Messenger及Handler進(jìn)行通信的方法示例
這篇文章主要介紹了Android開發(fā)使用Messenger及Handler進(jìn)行通信的方法,結(jié)合實(shí)例形式分析了Android使用Messenger及Handler定義客戶端與服務(wù)器端實(shí)現(xiàn)通信的相關(guān)操作技巧,需要的朋友可以參考下2017-12-12