kotlin 中的委托模式依靠by關(guān)鍵字,語(yǔ)法定義
val/var <Property name>?:<Type>by? <expression>
var/val:屬性類型(可變/只讀)
name:屬性名稱
Type:屬性的數(shù)據(jù)類型
expression:代理類
使用場(chǎng)景:
A.延遲加載屬性(lazy property): 屬性值只在初次訪問(wèn)時(shí)才會(huì)計(jì)算,
B.可觀察屬性(observable property): 屬性發(fā)生變化時(shí), 可以向監(jiān)聽(tīng)器發(fā)送通知,
C.將多個(gè)屬性保存在一個(gè) map 內(nèi), 而不是保存在多個(gè)獨(dú)立的域內(nèi).
---------------------------------------------------------------------------------------------------------------------
A.延遲加載(Lazy)
lazy()是一個(gè)函數(shù), 接受一個(gè)Lambda表達(dá)式作為參數(shù), 返回一個(gè)Lazy類型的實(shí)例,這個(gè)實(shí)例可以作為一個(gè)委托, 實(shí)現(xiàn)延遲加載屬性(lazy property): 第一次調(diào)用 get() 時(shí), 將會(huì)執(zhí)行 lazy() 函數(shù)受到的Lambda 表達(dá)式,然后會(huì)記住這次執(zhí)行的結(jié)果, 以后所有對(duì) get() 的調(diào)用都只會(huì)簡(jiǎn)單地返回以前記住的結(jié)果.
val no: Int by lazy {
? ? 200
}
val c = 200
fun main(args: Array) {
? ? val b = 200
? ? println(no) // Log : 200
? ? println(no) // Log : 200
}
注意:
1.var類型屬性不能設(shè)置為延遲加載屬性,因?yàn)樵趌azy中并沒(méi)有setValue(…)方法
2.lazy操作符是線程安全的。如果在不考慮多線程問(wèn)題或者想提高更多的性能,也可以使?
用 lazy(LazyThreadSafeMode.NONE){ … } 。
在LazyThreadSafetyMode中聲明了幾種,[Lazy]實(shí)例在多個(gè)線程之間同步訪問(wèn)的形式:
SYNCHRONIZED:鎖定,用于確保只有一個(gè)線程可以初始化[Lazy]實(shí)例。
PUBLICATION:初始化函數(shù)可以在并發(fā)訪問(wèn)未初始化的[Lazy]實(shí)例值時(shí)調(diào)用幾次,,但只有第一個(gè)返回的值將被用作[Lazy]實(shí)例的值。
NONE:沒(méi)有鎖用于同步對(duì)[Lazy]實(shí)例值的訪問(wèn); 如果從多個(gè)線程訪問(wèn)實(shí)例,是線程不安全的。此模式應(yīng)僅在高性能至關(guān)重要,并且[Lazy]實(shí)例被保證永遠(yuǎn)不會(huì)從多個(gè)線程初始化時(shí)使用。
B1.可觀察屬性(Observable)
Delegates.observable() 函數(shù)接受兩個(gè)參數(shù): 第一個(gè)是初始化值, 第二個(gè)是屬性值變化事件的響應(yīng)器(handler).這種形式的委托,采用了觀察者模式,其會(huì)檢測(cè)可觀察屬性的變化,當(dāng)被觀察屬性的setter()方法被調(diào)用的時(shí)候,響應(yīng)器(handler)都會(huì)被調(diào)用(在屬性賦值處理完成之后)并自動(dòng)執(zhí)行執(zhí)行的lambda表達(dá)式,同時(shí)響應(yīng)器會(huì)收到三個(gè)參數(shù):被賦值的屬性, 賦值前的舊屬性值, 以及賦值后的新屬性值。
var name: String by Delegates.observable("wang", {
? ? kProperty, oldName, newName ->
? ? println("kProperty:${kProperty.name} | oldName:$oldName | newName:$newName")
})
fun main(args: Array) {
? ? println("name: $name") // Log:nam:wang
? ? name = "zhang" // Log:kProperty:name | oldName:wang | newName:zhang
? ? name = "li" // Log:kProperty:name | oldName:zhang | newName:li
}
在這個(gè)例子中,Delegates.observable(wang, hanler),完成了兩項(xiàng)工作,一是,將name初始化(name=wang);二是檢測(cè)name屬性值的變化,每次變化時(shí),都會(huì)打印其賦值前的舊屬性值, 以及賦值后的新屬性值。
B2.Vetoable
Delegates.vetoable()函數(shù)接受兩個(gè)參數(shù): 第一個(gè)是初始化值, 第二個(gè)是屬性值變化事件的響應(yīng)器(handler),是可觀察屬性(Observable)的一個(gè)特例,不同的是在響應(yīng)器指定的自動(dòng)執(zhí)行執(zhí)行的lambda表達(dá)式中在保存新值之前做一些條件判斷,來(lái)決定是否將新值保存。
var name: String by Delegates.vetoable("wang", {
? ? kProperty, oldValue, newValue ->
? ? println("oldValue:$oldValue | newValue:$newValue")
? ? newValue.contains("wang")
})
fun main(args: Array) {
? ? println("name: $name")
? ? println("------------------")
? ? name = "zhangLing"
? ? println("name: $name")
? ? println("------------------")
? ? name = "wangBing"
? ? println("name: $name")
}
//Log
name: wang
------------------
oldValue:wang | newValue:zhangLing
name: wang
------------------
oldValue:wang | newValue:wangBing
name: wangBing
代碼示例中的委托,在給name賦值是,只有字符串中含有”wang”時(shí),將新值賦值給name.第一次給name賦值“zhangLing”時(shí),lambda表達(dá)式的返回值為false,此時(shí)并沒(méi)有對(duì)name成功賦值。而第二次,賦值”wangBing” 時(shí),lambda表達(dá)式的返回值為true,成功賦值。
B3.Not Null
在實(shí)際開(kāi)發(fā)時(shí),我們可能會(huì)設(shè)置可為null的var類型屬性,在我們使用它時(shí),肯定是對(duì)其賦值,假如不賦值,必然要報(bào)NullPointException.一種解決方案是,我們可以在使用它時(shí),在每個(gè)地方不管是不是null,都做null檢查,這樣我們就保證了在使用它時(shí),保證它不是null。這樣無(wú)形當(dāng)中添加了很多重復(fù)的代碼。
在Kotlin中,委托又幫我們做了一個(gè)善事,不用去寫這些重復(fù)的代碼,Not Null委托會(huì)含有一個(gè)可null的變量并會(huì)在我們?cè)O(shè)置這個(gè)屬性的時(shí)候分配一個(gè)真實(shí)的值。如果這個(gè)值在被獲取之前沒(méi)有被分配,它就會(huì)拋出一個(gè)異常。
class App : Application() {
? ? companion object {
? ? ? ? var instance: App by Delegates.notNull()
? ? }
? ? override fun onCreate() {
? ? ? ? super.onCreate()
? ? ? ? instance = this
? ? }
}
c.將多個(gè)屬性保存在一個(gè)map內(nèi)
使用Gson解析Json時(shí),可以獲取到相應(yīng)的實(shí)體類的實(shí)例,當(dāng)然該實(shí)體類的屬性名稱與Json中的key是一一對(duì)應(yīng)的。在Kotlin中,存在這么一種委托方式,類的構(gòu)造器接受一個(gè)map實(shí)例作為參數(shù),將map實(shí)例本身作為屬性的委托,屬性的名稱與map中的key是一致的,也就是意味著我們可以很簡(jiǎn)單的從一個(gè)動(dòng)態(tài)地map中創(chuàng)建一個(gè)對(duì)象實(shí)例。
Class1.
class User(val map: Map) {
? ? val name: String by map
? ? val age: Int by map
}
Class2.
fun main(args: Array) {
? ? val user = User(mapOf(
? ? ? ? ? ? "name" to "John Doe",
? ? ? ? ? ? "age" to 25
? ? ))
? ? println(user.name) // 打印結(jié)果為: "John Doe"
? ? println(user.age) // 打印結(jié)果為: 25
}
委托屬性將從這個(gè) map中讀取屬性值(使用屬性名稱字符串作為 key 值)。
如果不用只讀的 Map , 而改用值可變的 MutableMap , 那么也可以用作 var 屬性的委托。
class User(val map: MutableMap) {
? ? val name: String by map
? ? val age: Int by map
}
fun main(args: Array) {
? ? var map:MutableMap = mutableMapOf(
? ? ? ? ? ? "name" to "John Doe",
? ? ? ? ? ? "age" to 25)
? ? val user = User(map)
? ? println(user.name) // 打印結(jié)果為: "John Doe"
? ? println(user.age) // 打印結(jié)果為: 25
? ? println("--------------")
? ? map.put("name", "Green Dao")
? ? map.put("age", 30)
? ? println(user.name) // 打印結(jié)果為: Green Dao
? ? println(user.age) // 打印結(jié)果為: 30
}