[toc]
委托是什么
委托又可以稱為代理秉继。為其他對象提供一種代理以控制對這個對象的訪問,簡單的說就是在訪問和被訪問對象中間加上的一個間接層泽铛,以隔離訪問者和被訪問者的實現(xiàn)細節(jié)。
類委托
類委托其實對應于Java中的代理模式
interface Base{
fun text()
}
//被委托的類(真實的類)
class BaseImpl(val x :String ) : Base{
override fun text() {
println(x)
}
}
//委托類(代理類)
class Devices (b :Base) :Base by b
fun main(){
var b = BaseImpl("我是真實的類")
Devices(b).text()
}
輸出
我是真實的類
可以看到辑鲤,委托類(代理類)持有真實類的對象盔腔,然后委托類(代理類)調(diào)用真實類的同名方法,最終真正實現(xiàn)的是方法的是真實類月褥,這其實就是代理模式
kotlin中委托實現(xiàn)借助于by關(guān)鍵字弛随,by關(guān)鍵字后面就是被委托類,可以是一個表達式
接口委托
如果接口有很多方法宁赤,子類就必須全部實現(xiàn)舀透。
interface Api{
fun a()
fun b()
fun c()
}
class ApiWrapper:Api{
override fun a() {
}
override fun b() {
}
override fun c() {
}
}
kotlin可以找個對象代替你實現(xiàn)它,我們只需要實現(xiàn)需要的方法即可决左。
class ApiWrapper(val api:Api):Api by api{
override fun a() {
println("判斷是否登錄")
api.a()
}
}
設(shè)計一個好玩的例子
class SupperArray<T>(val list: MutableList<T> = mutableListOf(), val map: MutableMap<String, T> = mutableMapOf()) :
MutableList<T> by list, MutableMap<String, T> by map{
override fun clear() {
list.clear()
map.clear()
}
override fun isEmpty(): Boolean {
return list.isEmpty() && map.isEmpty()
}
override val size: Int
get() = list.size+map.size
override fun toString(): String {
return "$list $map"
}
}
這是一個超級集合愕够,包含一個list和一個map。這里用了代理佛猛,不需要實現(xiàn)全部的方法惑芭。只需要提供必要的方法。
fun main(args: Array<String>) {
val supperArray = SupperArray<String>()
supperArray["a"]= "hello"
supperArray.add("world")
println(supperArray.toString())
}
打印
[world] {a=hello}
屬性委托
屬性委托和類委托一樣继找,屬性的委托其實是對屬性的==set/get==方法的委托,把set/get方法委托給==setValue/getValue==方法,因此被委托類(真實類)需要提供==setValue/getValue==方法遂跟,val屬性只需要提供==setValue==方法
屬性委托語法:
val/var <屬性名>: <類型> by <表達式>
class B {
//委托屬性
var a : String by Text()
}
//被委托類(真實類)
class Text {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "屬性擁有者 = $thisRef, 屬性的名字 = '${property.name}' 屬性的值 "
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("屬性的值 = $value 屬性的名字 = '${property.name}' 屬性擁有者 = $thisRef")
}
}
fun main(){
var b = B()
println(b.a)
b.a = "ahaha"
}
輸出
屬性擁有者 = com.example.lib.weituo.B@27fa135a, 屬性的名字 = 'a' 屬性的值
屬性的值 = ahaha 屬性的名字 = 'a' 屬性擁有者 = com.example.lib.weituo.B@27fa135a
上面的例子可以看到 ,屬性a 委托給了Text,而且Text類中有setValue和getValue,所以當我們調(diào)用屬性a的get/set方法時候婴渡,會委托到Text的setValue/getValue上,上面的例子可以看出來,里面有幾個參數(shù)介紹一下:
thisRef:屬性的擁有者
property:對屬性的描述幻锁,是KProperty<*>類型或者父類
value:屬性的值
更加簡單的實現(xiàn)屬性委托
每次實現(xiàn)委托都要寫getValue/setValue方法,這就比較麻煩了,系統(tǒng)為我們提供了接口边臼,方便我們重寫這些方法,ReadOnlyProperty和ReadWriteProperty
interface ReadOnlyProperty<in R, out T> {
operator fun getValue(thisRef: R, property: KProperty<*>): T
}
interface ReadWriteProperty<in R, T> {
operator fun getValue(thisRef: R, property: KProperty<*>): T
operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
}
Koltin 標準庫中提供的幾個委托
延遲屬性(lazy properties):其值只在訪問時初始化哄尔,再次調(diào)用直接返回結(jié)果
可觀察屬性(observable properties):監(jiān)聽器會收到此屬性的變更通知
把多個屬性映射到(Map)中,而不是存在單個字段
延遲屬性lazy
lazy()接收一個lambda硼瓣,返回Lazy實例究飞,返回的實例可以作為實現(xiàn)延遲屬性的委托,僅在第一次調(diào)用屬性進行初始化
class Lazy {
val name :String by lazy {
println("第一次調(diào)用初始化")
"aa" }
}
fun main(){
var lazy =Lazy()
println(lazy.name)
println(lazy.name)
println(lazy.name)
}
輸出
第一次調(diào)用初始化
aa
aa
aa
lazy委托參數(shù)
SYNCHRONIZED:添加同步鎖堂鲤,使lazy延遲初始化線程安全
PUBLICATION:初始化的lambda表達式亿傅,可以在同一時間多次調(diào)用,但是只有第一次的返回值作為初始化值
NONE:沒有同步鎖瘟栖,非線程安全
1. by lazy是通過屬性代理來實現(xiàn)的懶加載葵擎,只在第一次使用的時候才會去執(zhí)行表達式,并且只會執(zhí)行一次半哟。
2. by lazy默認是線程安全的酬滤,內(nèi)部通過雙重判斷鎖來保證只執(zhí)行一次代碼塊賦值
3. 當能夠確定不會發(fā)生在多線程中的時候签餐,可通過lazy(LazyThreadSafetyMode.NONE) { ... }來避免加鎖。
lazy 原理
通過前面對屬性委托的簡單介紹盯串,我們也明白了屬性委托的機制氯檐。by后面跟著的肯定是一個對象,也就是委托對象体捏,該對象負責屬性的get/set冠摄,所以lazy這個方法最終肯定會返回一個對象。
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
public actual fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
when (mode) {
LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
}
public actual fun <T> lazy(lock: Any?, initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer, lock)
lazy方法一共有三個几缭,我們最常用的是第一個河泳。從代碼中可以看出,lazy方法最終是創(chuàng)建的Lazy<T>的實例年栓,這個實例也就是屬性委托的對象拆挥。
public interface Lazy<out T> {
public val value: T
public fun isInitialized(): Boolean
}
Lazy一共有三個子類,其中我們使用的第一個方法返回的是SynchronizedLazyImpl某抓,這是Lazy的其中一個實現(xiàn)纸兔。
internal object UNINITIALIZED_VALUE
private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
private var initializer: (() -> T)? = initializer
// 委托的屬性的值由_value記錄,初始值是單例對象UNINITIALIZED_VALUE
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
// final field is required to enable safe publication of constructed instance
private val lock = lock ?: this
override val value: T
get() {
val _v1 = _value
// 已初始化則直接返回對應值
if (_v1 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST")
return _v1 as T
}
// 未初始化的否副,執(zhí)行傳遞進來的lambda參數(shù)進行賦值
return synchronized(lock) {
val _v2 = _value
if (_v2 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST") (_v2 as T)
} else {
val typedValue = initializer!!()
_value = typedValue
initializer = null
typedValue
}
}
}
override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
private fun writeReplace(): Any = InitializedLazyImpl(value)
}
SynchronizedLazyImpl是Lazy的一個子類食拜,可以看到在value屬性的get方法中,會去判斷是否已經(jīng)初始化過副编,若是沒有初始化负甸,則會調(diào)用initializer去進行初始化,賦值給_value痹届。若是已經(jīng)初始化過呻待,則會直接返回 _value的值。因此by lazy就能通過這種方式去實現(xiàn)懶加載队腐,并且只加載一次蚕捉。
從代碼中可以看出,SynchronizedLazyImpl是保證了線程安全的柴淘。是通過DCL方式來保證的安全迫淹,能夠確保在多線程下也只會執(zhí)行一次代碼塊。
但是我們說過为严,要實現(xiàn)可讀屬性委托敛熬,必須實現(xiàn)getValue方法。而在SynchronizedLazyImpl中第股,并沒有實現(xiàn)getValue方法应民,而是只有value屬性的get方法。這里猜測編譯器應該是對Lazy有特殊的處理,而通過實驗诲锹,實現(xiàn)了Lazy接口的對象確實可以作為只讀屬性的委托對象繁仁。而其他接口即使與Lazy一模一樣,實現(xiàn)它的對象也不能作為屬性委托對象归园。
可觀察屬性O(shè)bservable委托
可以觀察一個屬性的變化過程
class Lazy {
var a : String by Delegates.observable("默認值"){
property, oldValue, newValue ->
println( "$oldValue -> $newValue ")
}
}
fun main(){
var lazy =Lazy()
lazy.a = "第一次修改"
lazy.a = "第二次修改"
}
輸出
默認值 -> 第一次修改
第一次修改 -> 第二次修改
vetoable委托
vetoable和Observable一樣,可以觀察屬性的變化黄虱,不同的是vetoable可以決定是否使用新值
class C {
var age: Int by Delegates.vetoable(0) { property, oldValue, newValue ->
println("oldValue = $oldValue -> oldValue = $newValue" )
newValue > oldValue
}
}
fun main() {
var c = C()
c.age = 5
println(c.age)
c.age = 10
println(c.age)
c.age = 8
println(c.age)
c.age = 20
println(c.age)
}
輸出
oldValue = 0 -> oldValue = 5
5
oldValue = 5 -> oldValue = 10
10
oldValue = 10 -> oldValue = 8
10
oldValue = 10 -> oldValue = 20
20
當新值小于舊值,那么就不生效庸诱,可以看到第三次設(shè)的值是8悬钳,小于10就沒有生效
屬性儲存在Map中
class D(val map: Map<String, Any?>) {
val name: String by map
val age: Int by map
}
fun main(){
var d = D(mapOf(
"name" to "小明",
"age" to 12
))
println("name = ${d.name},age = ${d.age}")
}
輸出
name = 小明,age = 12