fun receiverType.functionName(params){ body}
receiverType:表示函數(shù)的接收者,也就是函數(shù)擴(kuò)展的對象
functionName:擴(kuò)展函數(shù)的名稱
params:擴(kuò)展函數(shù)的參數(shù),可以為NULL
以下實(shí)例擴(kuò)展 User 類 :
class User(var name:String)/**擴(kuò)展函數(shù)**/fun User.Print(){ print("用戶名 $name")}fun main(arg:Array<String>){ var user = User("Runoob") user.Print()}
實(shí)例執(zhí)行輸出結(jié)果為:
用戶名 Runoob
下面代碼為 MutableList 添加一個swap 函數(shù):
// 擴(kuò)展函數(shù) swap,調(diào)換不同位置的值fun MutableList<Int>.swap(index1: Int, index2: Int) { val tmp = this[index1] // this 對應(yīng)該列表 this[index1] = this[index2] this[index2] = tmp}fun main(args: Array<String>) { val l = mutableListOf(1, 2, 3) // 位置 0 和 2 的值做了互換 l.swap(0, 2) // 'swap()' 函數(shù)內(nèi)的 'this' 將指向 'l' 的值 println(l.toString())}
實(shí)例執(zhí)行輸出結(jié)果為:
[3, 2, 1]
this關(guān)鍵字指代接收者對象(receiver object)(也就是調(diào)用擴(kuò)展函數(shù)時, 在點(diǎn)號之前指定的對象實(shí)例)。
擴(kuò)展函數(shù)是靜態(tài)解析的
擴(kuò)展函數(shù)是靜態(tài)解析的,并不是接收者類型的虛擬成員,在調(diào)用擴(kuò)展函數(shù)時,具體被調(diào)用的的是哪一個函數(shù),由調(diào)用函數(shù)的的對象表達(dá)式來決定的,而不是動態(tài)的類型決定的:
open class Cclass D: C()fun C.foo() = "c" // 擴(kuò)展函數(shù) foofun D.foo() = "d" // 擴(kuò)展函數(shù) foofun printFoo(c: C) { println(c.foo()) // 類型是 C 類}fun main(arg:Array<String>){ printFoo(D())}
實(shí)例執(zhí)行輸出結(jié)果為:
c
若擴(kuò)展函數(shù)和成員函數(shù)一致,則使用該函數(shù)時,會優(yōu)先使用成員函數(shù)。
class C { fun foo() { println("成員函數(shù)") }}fun C.foo() { println("擴(kuò)展函數(shù)") }fun main(arg:Array<String>){ var c = C() c.foo()}
實(shí)例執(zhí)行輸出結(jié)果為:
成員函數(shù)
擴(kuò)展一個空對象
在擴(kuò)展函數(shù)內(nèi), 可以通過 this 來判斷接收者是否為 NULL,這樣,即使接收者為 NULL,也可以調(diào)用擴(kuò)展函數(shù)。例如:
fun Any?.toString(): String { if (this == null) return "null" // 空檢測之后,“this”會自動轉(zhuǎn)換為非空類型,所以下面的 toString() // 解析為 Any 類的成員函數(shù) return toString()}fun main(arg:Array<String>){ var t = null println(t.toString())}
實(shí)例執(zhí)行輸出結(jié)果為:
null
擴(kuò)展屬性
除了函數(shù),Kotlin 也支持屬性對屬性進(jìn)行擴(kuò)展:
val <T> List<T>.lastIndex: Int get() = size - 1
擴(kuò)展屬性允許定義在類或者kotlin文件中,不允許定義在函數(shù)中。初始化屬性因?yàn)閷傩詻]有后端字段(backing field),所以不允許被初始化,只能由顯式提供的 getter/setter 定義。
val Foo.bar = 1 // 錯誤:擴(kuò)展屬性不能有初始化器
擴(kuò)展屬性只能被聲明為 val。
伴生對象的擴(kuò)展
如果一個類定義有一個伴生對象 ,你也可以為伴生對象定義擴(kuò)展函數(shù)和屬性。
伴生對象通過"類名."形式調(diào)用伴生對象,伴生對象聲明的擴(kuò)展函數(shù),通過用類名限定符來調(diào)用:
class MyClass { companion object { } // 將被稱為 "Companion"}fun MyClass.Companion.foo() { println("伴隨對象的擴(kuò)展函數(shù)")}val MyClass.Companion.no: Int get() = 10fun main(args: Array<String>) { println("no:${MyClass.no}") MyClass.foo()}
實(shí)例執(zhí)行輸出結(jié)果為:
no:10伴隨對象的擴(kuò)展函數(shù)
擴(kuò)展的作用域
通常擴(kuò)展函數(shù)或?qū)傩远x在頂級包下:
package foo.bar fun Baz.goo() { …… }
要使用所定義包之外的一個擴(kuò)展, 通過import導(dǎo)入擴(kuò)展的函數(shù)名進(jìn)行使用:
package com.example.usageimport foo.bar.goo // 導(dǎo)入所有名為 goo 的擴(kuò)展 // 或者import foo.bar.* // 從 foo.bar 導(dǎo)入一切fun usage(baz: Baz) { baz.goo()}
擴(kuò)展聲明為成員
在一個類內(nèi)部你可以為另一個類聲明擴(kuò)展。
在這個擴(kuò)展中,有個多個隱含的接受者,其中擴(kuò)展方法定義所在類的實(shí)例稱為分發(fā)接受者,而擴(kuò)展方法的目標(biāo)類型的實(shí)例稱為擴(kuò)展接受者。
class D { fun bar() { println("D bar") }}class C { fun baz() { println("C baz") } fun D.foo() { bar() // 調(diào)用 D.bar baz() // 調(diào)用 C.baz } fun caller(d: D) { d.foo() // 調(diào)用擴(kuò)展函數(shù) }}fun main(args: Array<String>) { val c: C = C() val d: D = D() c.caller(d)}
實(shí)例執(zhí)行輸出結(jié)果為:
D bar C baz
在 C 類內(nèi),創(chuàng)建了 D 類的擴(kuò)展。此時,C 被成為分發(fā)接受者,而 D 為擴(kuò)展接受者。從上例中,可以清楚的看到,在擴(kuò)展函數(shù)中,可以調(diào)用派發(fā)接收者的成員函數(shù)。
假如在調(diào)用某一個函數(shù),而該函數(shù)在分發(fā)接受者和擴(kuò)展接受者均存在,則以擴(kuò)展接收者優(yōu)先,要引用分發(fā)接收者的成員你可以使用限定的 this 語法。
class D { fun bar() { println("D bar") }}class C { fun bar() { println("C bar") } // 與 D 類 的 bar 同名 fun D.foo() { bar() // 調(diào)用 D.bar(),擴(kuò)展接收者優(yōu)先 this@C.bar() // 調(diào)用 C.bar() } fun caller(d: D) { d.foo() // 調(diào)用擴(kuò)展函數(shù) }}fun main(args: Array<String>) { val c: C = C() val d: D = D() c.caller(d)}
實(shí)例執(zhí)行輸出結(jié)果為:
D bar C bar
以成員的形式定義的擴(kuò)展函數(shù), 可以聲明為 open , 而且可以在子類中覆蓋. 也就是說, 在這類擴(kuò)展函數(shù)的派 發(fā)過程中, 針對分發(fā)接受者是虛擬的(virtual), 但針對擴(kuò)展接受者仍然是靜態(tài)的。
open class D {}class D1 : D() {}open class C { open fun D.foo() { println("D.foo in C") } open fun D1.foo() { println("D1.foo in C") } fun caller(d: D) { d.foo() // 調(diào)用擴(kuò)展函數(shù) }}class C1 : C() { override fun D.foo() { println("D.foo in C1") } override fun D1.foo() { println("D1.foo in C1") }}fun main(args: Array<String>) { C().caller(D()) // 輸出 "D.foo in C" C1().caller(D()) // 輸出 "D.foo in C1" —— 分發(fā)接收者虛擬解析 C().caller(D1()) // 輸出 "D.foo in C" —— 擴(kuò)展接收者靜態(tài)解析}
實(shí)例執(zhí)行輸出結(jié)果為:
D.foo in C D.foo in C1 D.foo in C
applixy
org***126.com
伴生對象內(nèi)的成員相當(dāng)于 Java 中的靜態(tài)成員,其生命周期伴隨類始終,在伴生對象內(nèi)部可以定義變量和函數(shù),這些變量和函數(shù)可以直接用類名引用。
對于伴生對象擴(kuò)展函數(shù),有兩種形式,一種是在類內(nèi)擴(kuò)展,一種是在類外擴(kuò)展,這兩種形式擴(kuò)展后的函數(shù)互不影響(甚至名稱都可以相同),即使名稱相同,它們也完全是兩個不同的函數(shù),并且有以下特點(diǎn):
例如以下代碼:
(1)類內(nèi)擴(kuò)展的伴隨對象函數(shù)和類外擴(kuò)展的伴隨對象可以同名,它們是兩個獨(dú)立的函數(shù),互不影響;
(2)當(dāng)類內(nèi)擴(kuò)展的伴隨對象函數(shù)和類外擴(kuò)展的伴隨對象同名時,類內(nèi)的其它函數(shù)優(yōu)先引用類內(nèi)擴(kuò)展的伴隨對象函數(shù),即對于類內(nèi)其它成員函數(shù)來說,類內(nèi)擴(kuò)展屏蔽類外擴(kuò)展;
(3)類內(nèi)擴(kuò)展的伴隨對象函數(shù)只能被類內(nèi)的函數(shù)引用,不能被類外的函數(shù)和伴隨對象內(nèi)的函數(shù)引用;
(4)類外擴(kuò)展的伴隨對象函數(shù)可以被伴隨對象內(nèi)的函數(shù)引用,;