一篇文章弄懂kotlin的擴展方法
Usage
擴展函數(shù)是 kotlin 的又一殺手锏功能,能夠在不修改源碼的基礎(chǔ)上,擴展某些類的能力,方便開發(fā)。
例如這里演示了給 String 添加一個獲取第一個元素的方法。
fun String.first(): Char { if (isEmpty()) { throw NoSuchElementException("String is empty") } return this[0] } fun main(args: Array<String>) { println("Hello,World".first()) }
這里需要額外注意的地方在于擴展函數(shù)的方法體中,是能夠直接訪問擴展對象 public 的變量的。例如上面的方法里面,我們也可以這么寫:
fun String.first(): Char { if (length < 1) { throw NoSuchElementException("String is empty") } return this[0] }
通過 this 可以在方法內(nèi),訪問擴展對象,這里就是通過 this[0] 拿到第一個字符的。
Under in hood
看上去很厲害哈,但他的原理卻非常簡單。我們要時刻記住,kotlin JVM 是基于 JVM 開發(fā)的,kotlin 源碼最后會變成字節(jié)碼而后被運行。當遇到語法上不太懂的地方,直接反編譯字節(jié)碼,或者 Decompile 成 Java 方法,就能洞察里面的玄機。
我們將上述代碼,Decompile 成 Java 后,就能發(fā)現(xiàn)里面的秘密。
public static final char first(@NotNull String $this$first){ Intrinsics.checkParameterIsNotNull($this$first, "$this$first"); if ($this$first.length() < 1) { throw (Throwable)(new NoSuchElementException("String is empty")); } else { return $this$first.charAt(0); } }
原來是生成了一個 public static final 的方法呀,不過這個生成是 kotlin 提供的語法糖,幫我們完成的??吹竭@個代碼,也解釋了為什么在擴展對象方法內(nèi)部,能夠訪問到擴展對象的 public 成員。
重載與多態(tài)
擴展方法能否被繼承呢,或者重載呢?我們來看看例子
open class Animal class Dog : Animal() fun Animal.desc() = "Animal" fun Dog.desc() = "Dog" fun main(args: Array<String>) { println(Dog().desc()) var animal: Animal = Dog() println(animal.desc()) } // output: // Dog // Animal
如果擴展方法能夠被重載,那么兩次都應(yīng)該輸出 Dog,我們還是和前面方法一樣,來看看真相。
@NotNull public static final String desc(@NotNull Animal $this$desc) { Intrinsics.checkParameterIsNotNull($this$desc, "$this$desc"); return "Animal"; } @NotNull public static final String desc(@NotNull Dog $this$desc) { Intrinsics.checkParameterIsNotNull($this$desc, "$this$desc"); return "Dog"; }
可以看到實際生成了兩個 desc 方法,里面的參數(shù)不動,所以這個方法的調(diào)用,只與擴展對象本身有關(guān)系,在編譯時已經(jīng)確定,不存在多態(tài)。
擴展屬性
這是一個很神奇的設(shè)定,kotlin 并不能真的給擴展對象添加一個屬性,而只是提供了一個語法糖,什么意思呢?我們具體看看下面這個例子。
var String.first: Char get() { if (isEmpty()) { throw NoSuchElementException(“String is empty”) } return this[0] } set(value) { println(“set value to $value”) } fun main() { “Hello, World”.first = ‘G' println(“Hello,World”.first) }
我們擴展了 kotlin 的屬性,添加了一個 first。我們可以分別給這個所謂的 first 屬性,注意是所謂的,添加 get 和 set 方法。然后我們可以通過 = 和 . 來調(diào)用 set 和 get 方法,就像 main 方法中那樣。但實際上,最后并沒有生成 first 屬性,我們來看看反編譯過后的代碼。
public static final Char getFirst(@NotNull String $this$first) { Intrinsics.checkParameterIsNotNull($this$first, "$this$first"); CharSequence var1 = (CharSequence)$this$first; boolean var2 = false; if (var1.length() == 0) { throw (Throwable)(new NoSuchElementException("String is empty")); } else { return $this$first.charAt(0); } } public static final void setFirst(@NotNull String $this$first, char value) { Intrinsics.checkParameterIsNotNull($this$first, "$this$first"); String var2 = "set value to " + value; boolean var3 = false; System.out.println(var2); }
看到?jīng)]有,實際上只是添加了 setFirst 和 getFirst 兩個方法,并沒有實際的屬性添加上去。這也是 kotlin 提供給我們的語法糖之一,糖要吃,但也要小心蛀牙哦!
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對腳本之家的支持。
相關(guān)文章
Android應(yīng)用開發(fā):電話監(jiān)聽和錄音代碼示例
這篇文章主要介紹了Android應(yīng)用開發(fā)中電話監(jiān)聽和電話錄音的代碼實例,同時附錄了一個拍照、錄像的例子,需要的朋友可以參考下2014-04-04Android拼圖游戲 玩轉(zhuǎn)從基礎(chǔ)到應(yīng)用手勢變化
這篇文章主要介紹了Android拼圖游戲的實現(xiàn)方法,教大家玩轉(zhuǎn)從基礎(chǔ)到應(yīng)用手勢變化,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-10-10Android ReboundScrollView仿IOS拖拽回彈效果
這篇文章主要為大家詳細介紹了Android ReboundScrollView仿IOS拖拽回彈效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-11-11android studio3.0以上如何通過navicat訪問SQLite數(shù)據(jù)庫文件
這篇文章主要介紹了android studio3.0以上如何通過navicat訪問SQLite數(shù)據(jù)庫文件,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06Android EditText長按菜單中分享功能的隱藏方法
Android EditText控件是經(jīng)常使用的控件,下面這篇文章主要給大家介紹了關(guān)于Android中EditText長按菜單中分享功能的隱藏方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-02-02