歡迎關(guān)注 二師兄Kotlin
轉(zhuǎn)載請注明出處 二師兄kotlin
擴展(Extensions)
前文講過胰锌,Kotlin, 跟C#和Gosu很像,提供了便捷的為類擴展函數(shù)的能力巴刻,而你并不需要繼承于它,或者使用設(shè)計模式來包裝涛碑,比如Decorator裝飾者模式
. 我們使用一種特殊的聲明方式來完成這個工作 . Kotlin 支持函數(shù)和屬性的擴展.
擴展函數(shù)(Extentions Functions)
為了聲明一個函數(shù)擴展蚪腋,我們需要在函數(shù)前加一個接收者類型(receiver type)作為前綴。下面我們會為 MutableList<Int>
添加一個 swap
函數(shù):
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' corresponds to the list
this[index1] = this[index2]
this[index2] = tmp
}
在擴展函數(shù)中的this
關(guān)鍵字對應接收者對象⊥旨幔現(xiàn)在我們可以在任何 MutableList<Int>
實例中使用這個函數(shù)了:
val l = mutableListOf(1, 2, 3)
l.swap(0, 2)// 在 `swap()` 函數(shù)中 `this` 持有的值是 `l`
當然,這個函數(shù)對任意的 MutableList<T>
都是適用的搏色,而且我們可以把它變的通用:
fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' corresponds to the list
this[index1] = this[index2]
this[index2] = tmp
}
我們在函數(shù)名前聲明了通用類型,從而使它可以接受任何參數(shù)券册。參看
泛型函數(shù)频轿。
擴展是被靜態(tài)解析的
然而,Kotlin并不實際修改它所擴展的類烁焙,給某個類定義了一個擴展函數(shù)也并沒有實際為這個插入任何函數(shù)代碼航邢,僅僅是增加了一個這個類的調(diào)用函數(shù)的途徑,調(diào)用方式xx.extentionsFunc骄蝇。
需要強調(diào)的是擴展函數(shù)是靜態(tài)分發(fā)的膳殷,舉個例子,它們并不是接受者類型的虛擬方法。這意味著擴展函數(shù)的調(diào)用是由發(fā)起函數(shù)調(diào)用的表達式的類型決定的九火,而不是在運行時動態(tài)獲得的表達式的類型決定赚窃。比如
舉例:
open class C
class D: C()
fun C.foo() = "c"
fun D.foo() = "d"
fun printFoo(c: C) {
println(c.foo())
}
printFoo(D())
例子中輸出的結(jié)果是c
册招。擴展函數(shù)被調(diào)用時,傳入的參數(shù)是c
勒极, 即類C是掰。
如果一個類擁有一個成員函數(shù),但是又定義了一個這個類的擴展函數(shù)和它的成員函數(shù)名字一模一樣辱匿,參數(shù)也一樣键痛,此時只執(zhí)行成員函數(shù)。
舉例:
class C {
fun foo() { println("member") }
}
fun C.foo() { println("extension") }
比如我們調(diào)用 類C
的任意實例c
的c.foo()
函數(shù) , 只會打印 “member”, 而不是“extension”.
與上述其他情況相同匾七,名字相同但是函數(shù)簽名不同時絮短,擴展函數(shù)工作完全正常,舉例:
class C {
fun foo() { println("member") }
}
fun C.foo(i: Int) { println("extension") }
調(diào)用 C().foo(1) 時將會輸出“extension”.
空接收者
注意擴展可以使用空接收者類型進行定義昨忆。這樣的擴展使得丁频,即使是一個空對象仍然可以調(diào)用該擴展,然后在擴展的內(nèi)部進行 this == null 的判斷扔嵌。這樣你就可以在 Kotlin 中任意調(diào)用 toString() 方法而不進行空指針檢查:空指針檢查延后到擴展函數(shù)中完成限府。
fun Any?.toString(): String {
if (this == null) return "null"
// after the null check, 'this' is autocast to a non-null type, so the toString()
below
// resolves to the member function of the Any class
return toString()
}
擴展屬性
類似擴展函數(shù),Kotlin同樣支持擴展屬性:
val <T> List<T>.lastIndex: Int
get() = size - 1
注意痢缎,擴展函數(shù)并未向類中插入函數(shù) , 擴展屬性在類中也并沒有支持字段(backing field
)胁勺。這就是為什么初始化程序(initializers)不允許擴展屬性。 只有在顯示提供getters/setters
后才可以定義擴展屬性独旷,舉例:
val Foo.bar = 1 // error: initializers are not allowed for extension properties
接下來會講述友元對象擴展以及擴展的作用域署穗。
前文講過,Kotlin, 跟C#和Gosu很像,提供了便捷的為類擴展函數(shù)的能力嵌洼,而你并不需要繼承于它案疲,或者使用設(shè)計模式來包裝,比如Decorator裝飾者模式
. 我們使用一種特殊的聲明方式來完成這個工作 . Kotlin 支持函數(shù)和屬性的擴展.
擴展函數(shù)(Extentions)的“靜態(tài)執(zhí)行”
然而麻养,Kotlin并不實際修改它所擴展的類褐啡,給某個類定義了一個擴展函數(shù)也并沒有實際為這個插入任何函數(shù)代碼,僅僅是增加了一個這個類的調(diào)用函數(shù)的途徑鳖昌,調(diào)用方式xx.extentionsFunc备畦。
需要強調(diào)的是擴展函數(shù)只能“在調(diào)用處靜態(tài)的工作”, 意思是調(diào)用處傳入的參數(shù)是誰就是誰,不支持多態(tài)许昨。
舉例:
open class C
class D: C()
fun C.foo() = "c"
fun D.foo() = "d"
fun printFoo(c: C) {
println(c.foo())
}
printFoo(D())
例子中輸出的結(jié)果是c
懂盐。擴展函數(shù)被調(diào)用時,傳入的參數(shù)是c
糕档, 即類C莉恼。
如果一個類擁有一個成員函數(shù),但是又定義了一個這個類的擴展函數(shù)和它的成員函數(shù)名字一模一樣,參數(shù)也一樣俐银,此時只執(zhí)行成員函數(shù)尿背。
舉例:
class C {
fun foo() { println("member") }
}
fun C.foo() { println("extension") }
比如我們調(diào)用 類C
的任意實例c
的c.foo()
函數(shù) , 只會打印 “member”, 而不是“extension”.
與上述其他情況相同,名字相同但是函數(shù)簽名不同時悉患,擴展函數(shù)工作完全正常残家,舉例:
class C {
fun foo() { println("member") }
}
fun C.foo(i: Int) { println("extension") }
調(diào)用 C().foo(1) 時將會輸出“extension”.
空接收者
注意擴展可以使用空接收類型,如此擴展就可以被一個即使值為null的變量所調(diào)用售躁,而且可以在擴展函數(shù)體中執(zhí)行檢查this == null
坞淮。這就是為什么在Kotlin中你不用檢查是否為null就可以調(diào)用toString
: 檢查發(fā)生在擴展函數(shù)中。
fun Any?.toString(): String {
if (this == null) return "null"
// after the null check, 'this' is autocast to a non-null type, so the toString()
below
// resolves to the member function of the Any class
return toString()
}
擴展屬性
類似擴展函數(shù)陪捷,Kotlin同樣支持擴展屬性:
val <T> List<T>.lastIndex: Int
get() = size - 1
注意回窘,擴展函數(shù)并未向類中插入函數(shù) , 擴展屬性在類中也并沒有支持字段(backing field
)让簿。這就是為什么初始化程序(initializers)不允許擴展屬性宣蠕。 只有在顯示提供getters/setters
后才可以定義擴展屬性吨拗,舉例:
val Foo.bar = 1 // error: initializers are not allowed for extension properties
接下來會講述友元對象擴展以及擴展的作用域统抬。