什么是擴(kuò)展函數(shù)
擴(kuò)展函數(shù)是 Java 不具備的,而 Kotlin 獨(dú)有的特性,在日常開發(fā)中使用頻率很高,類似于設(shè)計(jì)模式中的裝飾模式,其作用就是在不改變?cè)蓄惖那闆r下,擴(kuò)展新的功能。
如何使用擴(kuò)展函數(shù)和擴(kuò)展屬性
擴(kuò)展函數(shù)的基本使用
在需要被擴(kuò)展的類或者接口后面添加方法即可,下面是一個(gè)給 Int 類型擴(kuò)展了一個(gè)判斷是否是偶數(shù)的方法的例子,在擴(kuò)展函數(shù)里使用 this 指代被擴(kuò)展的類或者接口的實(shí)例對(duì)象:
/**
* 是否是偶數(shù),用與運(yùn)算取出最后一位,最后一位為0則是偶數(shù)
*/
fun Int.isEven(): Boolean {
return (this and 1) == 0
}
使用的時(shí)候,對(duì)于任意一個(gè) Int 型數(shù)據(jù)都可以直接調(diào)用 isEven() 方法。
2.isEven() //得到返回值true
擴(kuò)展屬性的基本使用
擴(kuò)展屬性實(shí)際上是提供一種方法來訪問屬性而已,并且這些擴(kuò)展屬性是沒有任何的狀態(tài)的,因?yàn)椴豢赡芙o被擴(kuò)展的類或者接口的對(duì)象額外添加屬性字段,只是使用簡(jiǎn)潔語(yǔ)法類似直接操作屬性,實(shí)際上還是方法的訪問。例如給 TextView 擴(kuò)展一個(gè)屬性表示是否是粗體:
//擴(kuò)展屬性定義
var TextView.isBolder: Boolean
get() {
return this.paint.isFakeBoldText
}
set(value) {
this.paint.isFakeBoldText = value
}
在擴(kuò)展屬性中也是使用 this 指代被擴(kuò)展的類或者接口的實(shí)例對(duì)象。另外必須定義 get() 方法,在 Kotlin 中類中的屬性都是默認(rèn)添加 get() 方法的,但是由于擴(kuò)展屬性并不是給現(xiàn)有類添加額外的屬性,自然就沒有默認(rèn) get() 方法實(shí)現(xiàn)了,所以必須手動(dòng)添加 get() 方法。至于 set() 方法,就看屬性是否可變了,也就是該擴(kuò)展屬性對(duì)應(yīng)是 var 還是 val 了。
什么是頂層函數(shù)
在解析擴(kuò)展函數(shù)的本質(zhì)之前,先來了解一下 Kotlin 的另一個(gè)特性 -- 頂層函數(shù)。
在 Java 中有靜態(tài)方法和靜態(tài)屬性,一般是為了提供全局共享訪問的方法和屬性,是獨(dú)立于對(duì)象之外的。靜態(tài)方法和靜態(tài)屬性需要在類中聲明,在使用的時(shí)候也是通過 類名.方法名 或者 類名.屬性名 的方式訪問。靜態(tài)函數(shù)內(nèi)部是不包含狀態(tài)的,也就是所謂的純函數(shù),它的輸入僅僅來自于它的參數(shù)列表,而它的輸出也僅僅依賴于它參數(shù)列表,靜態(tài)函數(shù)所在的類只是作為一個(gè)容器的角色。
在 Kotlin 中認(rèn)為一個(gè)函數(shù)有時(shí)候并不需要屬于任何一個(gè)類,它可以獨(dú)立存在。所以在 Kotlin 中類似靜態(tài)函數(shù)和靜態(tài)屬性可以去掉外層類的容器,一個(gè)函數(shù)或者屬性可以直接定義在一個(gè) Kotlin 文件的頂層中,在使用的地方只需要 import 這個(gè)函數(shù)或?qū)傩约纯桑@就是頂層函數(shù)。
頂層函數(shù)的使用
首先,創(chuàng)建一個(gè)文件,這里文件取名為 TopFunction:
然后在文件中定義函數(shù),這里定義了一個(gè)判斷 Int 數(shù)字是否是偶數(shù)的函數(shù):
/**
* 是否是偶數(shù),用與運(yùn)算取出最后一位,最后一位為0則是偶數(shù)
*/
fun isEven(num: Int): Boolean {
return (num and 1) == 0
}
需要使用的時(shí)候直接調(diào)用函數(shù)即可:
isEven(2) //得到返回值true
定義并使用一個(gè)頂層函數(shù)很簡(jiǎn)單,但是頂層函數(shù)究竟是怎么運(yùn)行在 JVM 中的呢?如果是 Java 和 Kotlin 混合開發(fā)模式,在 Kotlin 中定義的頂層函數(shù),在 Java 中又是怎么調(diào)用的呢?
頂層函數(shù)的本質(zhì)
通過 decompile 看下反編譯后對(duì)應(yīng)的 Java 代碼:
public final class TopFunctionKt {
public static final boolean isEven(int num) {
return (num & 1) == 0;
}
}
- 頂層文件會(huì)反編譯成一個(gè)容器類。(類名默認(rèn)為頂層文件名+"Kt"后綴)
- 頂層函數(shù)會(huì)反編譯成一個(gè) static 靜態(tài)函數(shù)。
所以頂層函數(shù)本質(zhì)就是 Java 中的靜態(tài)函數(shù)。如果需要在 Java 中調(diào)用 Kotlin 中的頂層函數(shù),方式很簡(jiǎn)單,就是利用反編譯生成的類作為靜態(tài)函數(shù)容器類直接調(diào)用對(duì)應(yīng)的函數(shù),例如上面的頂層函數(shù)在 Java 中調(diào)用寫法,TopFunctionKt.isEven(int num)。
注意: 頂層文件反編譯成的 Java 中的容器類名默認(rèn)是頂層文件名+“Kt”后綴,但是也是可以自定義的。也就是說頂層文件名和生成容器類名沒有必然的聯(lián)系。通過 Kotlin 中的 @file: JvmName("自定義生成類名") 注解就可以自動(dòng)生成對(duì)應(yīng) Java 調(diào)用類名,注意需要放在文件頂部,在package聲明的前面。這樣 Java 調(diào)用自定義類名頂層函數(shù)就更加自然,一般建議使用注解修改類名。
擴(kuò)展函數(shù)的本質(zhì)
擴(kuò)展函數(shù)也是不屬于任何一個(gè)類,獨(dú)立存在于 Kotlin 文件中,是不是和頂層函數(shù)一樣,本質(zhì)是 Java 中的靜態(tài)函數(shù)呢?答案是肯定的。
/**
* 是否是偶數(shù),用與運(yùn)算取出最后一位,最后一位為0則是偶數(shù)
*/
fun Int.isEven(): Boolean {
return (this and 1) == 0
}
我們將上述擴(kuò)展函數(shù)寫在文件名為 SpreadFunction 的文件中,通過 decompile 看下反編譯后對(duì)應(yīng)的 Java 代碼:
public final class SpreadFunctionKt {
public static final boolean isEven(int $this$isEven) {
return ($this$isEven & 1) == 0;
}
}
跟頂層函數(shù)如出一轍,擴(kuò)展函數(shù)本質(zhì)就是 Java 中的靜態(tài)函數(shù),被擴(kuò)展的類或者接口的實(shí)例對(duì)象會(huì)作為函數(shù)參數(shù),用 this 指代。同樣的,在 Java 中調(diào)用 Kotlin 中的擴(kuò)展函數(shù)也是一樣的方式,不同的地方是需要插入一個(gè)被擴(kuò)展的類或者接口的實(shí)例對(duì)象。
擴(kuò)展屬性實(shí)際上就是提供某個(gè)屬性訪問的set,get方法,set,get方法本質(zhì)都是靜態(tài)函數(shù),同時(shí)都會(huì)傳入一個(gè)被擴(kuò)展類的對(duì)象,然后在其內(nèi)部用這個(gè)實(shí)例對(duì)象去訪問和修改對(duì)象所對(duì)應(yīng)的類的屬性,在此就不細(xì)說了。
擴(kuò)展函數(shù)與成員函數(shù)
- 擴(kuò)展函數(shù)和成員函數(shù)都可以訪問被擴(kuò)展類的公有方法和屬性。
- 擴(kuò)展函數(shù)不能訪問被擴(kuò)展類的私有方法和屬性,成員函數(shù)可以訪問類中的私有方法和屬性。
- 父類成員函數(shù)可以被子類重寫,但擴(kuò)展函數(shù)不可以被子類重寫。
理解了擴(kuò)展函數(shù)的本質(zhì)是一個(gè)靜態(tài)函數(shù),且處于類的外部,被擴(kuò)展類的實(shí)例對(duì)象作為函數(shù)參數(shù)傳入,就可以很好地理解擴(kuò)展函數(shù)與成員函數(shù)之間的共同性和差異性了。擴(kuò)展函數(shù)通過實(shí)例對(duì)象進(jìn)行訪問自然不能訪問類中的私有方法和屬性,但可以訪問類中的公有方法和屬性。擴(kuò)展函數(shù)處于類的外部,并不是類的一部分,不會(huì)被子類所繼承,自然不可以被子類重寫。