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