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