前言
即,相同的事情,執行人從我 交給了 你。
我 不需要改動 我的部分,修改就交給你完成。增加了靈活性。
本節要關注幾個問題。
① 委託是通過什麼實現的?
② 實現委託有幾個步驟,幾個組成部分
③ 委託可以改變什麼?屬性?函數重載?
④ lazy懶加載如何實現?要注意什麼?加載幾次?什麼時候加載?
⑤ Observable觀察者模式是怎麼樣的?
⑥ val&var在委託時,要注意什麼
⑦ provideDelegate是如何實現的?
⑧ 屬性映射如何實現?
包括了一下幾個部分
- 簡介
- 所謂委託
- 類委託
- 屬性委託
- 標準委託
- 把屬性儲存在映射中
- Not Null
- 局部委託屬性
- 屬性委託要求
- 小結
簡介
Kotlin 直接支持委託模式,更加優雅,簡潔;
Kotlin 通過關鍵字 by 實現委託
所謂委託
就是在原來調用的對象上,包了一層
通過 包裹層調用 原來的對象
是 實現繼承的一個很好的替代方法
類委託
這有3個部分
- interface
- interface的實現
- 委託類
- 外➕實際調用範例
用範例來看下 委託的3個部分
// 創建接口
interface Base {
fun print()
}
// 實現此接口的被委託的類
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
// 通過關鍵字 by 建立委託類
class Derived(b: Base) : Base by b
fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print() // 輸出 10
}
當然委託也支持重載
// 通過關鍵字 by 建立委託類
class Derived(b: Base) : Base by b
改成
// 通過關鍵字 by 建立委託類
class Derived(b: Base) : Base by b {
override fun print() { print("abc") }
fun print2() {println("the result of print2")}
}
輸出就變成了abc
屬性委託
val/var <屬性名>: <類型> by <表達式>
不必實現任何接口,
但必須提供 getValue() 函數(對於 var屬性,還需要 setValue() 函數),定義一個被委託的類
範例
import kotlin.reflect.KProperty
// 定義包含屬性委託的類
class Example {
var p: String by Delegate()
}
// 委託的類
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, 這裏委託了 ${property.name} 屬性"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$thisRef 的 ${property.name} 屬性賦值爲 $value")
}
}
fun main(args: Array<String>) {
val e = Example()
println(e.p) // 訪問該屬性,調用 getValue() 函數
e.p = "Runoob" // 調用 setValue() 函數
println(e.p)
}
標準委託
延遲屬性 Lazy
範例
val lazyValue: String by lazy {
println("computed!") // 第一次調用輸出,第二次調用不執行
"Hello"
}
fun main(args: Array<String>) {
println(lazyValue) // 第一次執行,執行兩次輸出表達式
println(lazyValue) // 第二次執行,只輸出返回值
}
輸出
computed!
Hello
Hello
lazy是系統提供的函數
提供了同步鎖,是線程安全的;
是不是突然覺得 各種延遲初始化,變得很清晰,實現還很方便
可觀察屬性 Observable
observable 可以用於實現觀察者模式
Delegates.observable() 函數接受兩個參數
- 第一個是初始化值
- 第二個是屬性值變化事件的響應器(handler)
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("初始值") {
prop, old, new ->
println("舊值:$old -> 新值:$new")
}
}
fun main(args: Array<String>) {
val user = User()
user.name = "第一次賦值"
user.name = "第二次賦值"
}
把屬性儲存在映射中
這部分在《Kotlin入門-屬性》中有過講解。
來複習一下。
name\url是需要初始值的,否則會編譯出錯
通過將name、url交給map自身處理,達到延遲初始化的效果
複習一下案例
val user = User(mapOf(
"name" to "John Doe",
"age" to 25
))
輸出
println(user.name) // Prints "John Doe"
println(user.age) // Prints 25
Not Null
適用於那些無法在初始化階段就確定屬性值的場合
class Foo {
var notNullBar: String by Delegates.notNull<String>()
}
foo.notNullBar = "bar"
println(foo.notNullBar)
注意
不能在賦值前,被訪問
局部委託屬性
局部變量的延遲初始化
fun example(computeFoo: () -> Foo) {
val memoizedFoo by lazy(computeFoo)
if (someCondition && memoizedFoo.isValid()) {
memoizedFoo.doSomething()
}
}
首先定義了 by lazy(computeFoo)
注意
- 這裏只會進行一次初始化
- 如果 someCondition 失敗,那麼該變量根本不會計算
屬性委託要求
必須是同類型、超類型
- 對於 val 只讀屬性,委託必須提供一個名爲 getValue 的函數
- 對於 var 可變屬性,委託必須額外提供一個名爲 setValue 的函數
翻譯規則
每個委託屬性的實現的背後
Kotlin 編譯器都會生成輔助屬性並委託給它
編譯後的範例學習
class C {
var prop: Type by MyDelegate()
}
// 看這段
// 這段是由編譯器生成的相應代碼:
class C {
private val prop$delegate = MyDelegate()
var prop: Type
get() = prop$delegate.getValue(this, this::prop)
set(value: Type) = prop$delegate.setValue(this, this::prop, value)
}
注意看$之後的東西
代表了內部外部的引用關係
提供委託(自 1.1 起)
擴展創建屬性實現 所委託對象 的邏輯
理解
- 這提供了 委託類 的動態擴展能力
- 便方便的實現委託類的定製化(繼承之後)
- 也就增強了委託類的層次感
範例
class ResourceDelegate<T> : ReadOnlyProperty<MyUI, T> {
override fun getValue(thisRef: MyUI, property: KProperty<*>): T { ... }
}
class ResourceLoader<T>(id: ResourceID<T>) {
operator fun provideDelegate(
thisRef: MyUI,
prop: KProperty<*>
): ReadOnlyProperty<MyUI, T> {
checkProperty(thisRef, prop.name)
// 創建委託
return ResourceDelegate()
}
private fun checkProperty(thisRef: MyUI, name: String) { …… }
}
class MyUI {
fun <T> bindResource(id: ResourceID<T>): ResourceLoader<T> { …… }
val image by bindResource(ResourceID.image_id)
val text by bindResource(ResourceID.text_id)
}
thisRef
必須與 屬性所有者 類型(對於擴展屬性——指被擴展的類型)相同或者是它的超類型
property
必須是類型 KProperty<*> 或其超類型。
通過範例,可以看到委託解決了
- 顯式傳遞屬性名
- 提供了攔截的功能
看一段provideDelegate的生成代碼
class C {
var prop: Type by MyDelegate()
}
// 這段代碼是當“provideDelegate”功能可用時
// 由編譯器生成的代碼:
class C {
// 調用“provideDelegate”來創建額外的“delegate”屬性
private val prop$delegate = MyDelegate().provideDelegate(this, this::prop)
val prop: Type
get() = prop$delegate.getValue(this, this::prop)
}
注意
provideDelegate 方法隻影響輔助屬性的創建;
不會影響爲 getter 或 setter 生成的代碼
小結
Kotlin的委託,提供了延遲加載lazy、繼承的第二種實現、屬性&函數的快捷修改、觀察者模式的快捷實現。