Kotlin-Learning 扩展
與 C#,Gosu 類似。kotlin 可以不繼承父類,也不使用裝飾器模式對原有的類進行擴展,添加新的功能。
Kotlin 支持函數擴展和屬性擴展
擴展函數
package classandobject// MutableList<Int> 作為 swap 函數的接收者。 // 交換兩個下標的位置 fun MutableList<Int>.swap(index1: Int, index2: Int) {val temp = this[index1]this[index1] = this[index2]this[index2] = temp } 復制代碼擴展函數的前綴是這個函數的接收者。 上面的擴展函數給 MutableList 添加了一個新功能 swap, 交換 2 個數的位置。
擴展是被靜態解析的
擴展沒有修改它所擴展的類,定義一個擴展,并沒有插入一個新的成員。只是能讓這個類的實例通過 . 調用擴展函數。
擴展函數是靜態分發的。
舉個例子:
open class Cclass D:C()fun C.foo() = "c" fun D.foo() = "d"fun printFoo(c:C){println(c.foo()) }輸出:c 復制代碼雖然傳進了 D 的實例作為參數,foo 并不是 D 的一個成員函數,和 D 沒關系。這里由聲明參數的類型決定調用誰的方法,這里聲明了 C 類型,就會調用 C 的 foo()。
同名的成員函數優先級大于擴展函數:
class E {fun foo() {println("member")} }fun E.foo() {println("extension") } // 輸出 member 復制代碼接收者為空的情況
fun Any?.toString(): String {if (this == null) {return "null"}return toString() } 復制代碼要擴展的類可能為 null。可以對其做 null 的判斷處理。
擴展屬性
// 屬性擴展, 必須定義訪問器 val <T> List<T>.lastIndex: Intget() = size - 1復制代碼擴展屬性不會真正給類添加一個屬性,沒法讓這個屬性有一個 backing field,所以不可以初始化。只能通過 set,get 定義。
伴隨對象擴展
如果一個類定義了伴隨對象,也可以為這個伴隨對象定義擴展函數和擴展屬性。
// 為伴隨對象定義擴展函數和擴展屬性 class CompanionClass{companion object{// ...} }fun CompanionClass.Companion.foo(){println("CompanionClass.Companion.foo()") } 復制代碼使用 類名.方法() 直接調用:
CompanionClass.foo() 復制代碼擴展的范圍
大部分都是用在包名下。
package foo.barfun Baz.goo() { ... } 復制代碼用在別的包名下,需要導包 import
將擴展作為成員聲明
class D1 {fun bar(){//...println("D1.bar()")} }// 在 C1 類聲明 D1 類的擴展函數 foo() class C1 {private fun baz(){println("C1.baz()")}fun D1.foo(){bar() // calls D1.barbaz() // call C1.baz}fun caller(d:D1){d.foo()} } 復制代碼將 D1 的擴展函數作為成員聲明在 C1 中。此時 D1 叫 extension receiver.C1 負責分發,叫 dispatch receiver。
在 C1 中的 foo() 擴展函數既可以調用 D1 中的函數,也可以調用 C1 中的函數,如果同名的話,優先調用 D1 中的。 同名的屬性也是一樣。
為了調用 C1 中的同名函數,可以用 this 限定符。
class C {fun D.foo() {toString() // calls D.toString()this@C.toString() // calls C.toString()} 復制代碼dispath receiver 類型是動態的
定義在類中的擴展函數可以被子類重寫。
// receiver type 是靜態的。 dispatch receiver type 是動態的 open class Rclass R1 : R()open class T {open fun R.foo() {println("R.foo in T")}open fun R1.foo() {println("R1.foo in T")}fun caller(r: R) {r.foo()} }class T1 : T() {override fun R.foo() {println("R.foo in T1")}override fun R1.foo() {println("R1.foo in T1")} }// 調用 T().caller(R()) T().caller(R1())T1().caller(R()) T1().caller(R1())// 輸出 R.foo in T R.foo in T R.foo in T1 R.foo in T1 復制代碼前面也提到擴展函數動動態解析的。由于 caller 中參數定義了類型為 R 所以只會調用 R 的擴展函數。而分發者的類型是動態的。
為什么要有擴展函數
在 java 中我們會在一個類中寫很多靜態方法作為工具類。FileUtils,StringUtils...
在 Java 代碼中。有時候在一句代碼中需要使用一個工具類的多個方法,就會變成這樣:
Collections.swap(list, Collections.binarySearch(list, Collections.max(otherList)), Collections.max(list)); 復制代碼可以使用靜態導包(static import)的方式解決:
swap(list, binarySearch(list, max(otherList)), max(list)); 復制代碼list 看起來重復。
這樣寫更簡潔,把所有的方法都歸納到 list 內當中,就不需要每個地方都傳一個 list 對象。
list.swap(list.binarySearch(otherList.max()), list.max()); 復制代碼但是不可能在 List 中實現所有的方法。但是 Kotlin 的擴展可以做到,這就是為什么需要擴展的原因。
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的Kotlin-Learning 扩展的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Source Insight 4.0常用
- 下一篇: saltstack管理saltstack