编译器生成的字节码:

class Example {
private val prop$delegate = Delegate()
// 被委托属性
var prop: String
get() = prop$delegate.getValue(this, this:prop)
set(value : String) = prop$delegate.setValue(this, this:prop, value)
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
> **注意事项:**
> - thisRef —— 必须与属性所有者类型相同或者是它的超类型。
> - property —— 必须是类型 KProperty<*> 或其超类型。
> - value —— 必须和属性同类型或者是它的超类型。

### 局部变量委托
局部变量也可以声明委托,例如:
```kotlin
fun main(args: Array<String>) {
val lazyValue: String by lazy {
println("Lazy Init Completed!")
"Hello World."
}

if (true/*someCondition*/) {
println(lazyValue) // 首次调用
println(lazyValue) // 后续调用

}
}
输出:
Lazy Init Completed!
Hello World.
Hello World.

Kotlin 委托进阶

延迟属性委托 lazy

lazy 是一个标准库函数,参数为一个 Lambda 表达式,返回值为一个 Lazy 实例,使用 lazy 可以实现延迟属性委托,在委托对象比较耗资源的场景会非常有用。首次访问属性是,会执行 lazy 函数的 lambda 表达式并将结果记录到「背域」,后续调用 getter() 方法只是直接返回「背域」的值。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
val lazyValue: String by lazy {
println("Lazy Init Completed!")
"Hello World."
}

fun main(args: Array<String>) {
println(lazyValue) // 首次调用
println(lazyValue) // 后续调用
}

输出:
Lazy Init Completed!
Hello World.
Hello World.

可观察属性 ObservableProperty

使用 Delegates.observable() 可以实现可观察属性,函数接受两个参数:第一个参数为初始值,第二个参数为属性值变化的回调。函数的返回值是 ObservableProperty 可观察属性,它在调用 setValue(…) 是触发回调。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 = "第二次赋值"
}

输出:
旧值:初始值 -> 新值:第一次赋值
旧值:第一次赋值 -> 新值:第二次赋值

使用 Map 存储属性值

Map / MutableMap 也可以用来实现属性委托,从而此时字段名是 Key,属性值是 Value。例如;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class User(val map: Map<String, Any?>) {
val name: String by map
}

fun main(args: Array<String>) {
val map = mutableMapOf(
"name" to "彭"
)
val user = User(map)
println(user.name)
map["name"] = "peng"
println(user.name)
}
输出:

peng

4. 总结

Kotlin 委托的语法关键字是 by,其本质上是面向编译器的语法糖,三种委托(类委托、对象委托和局部变量委托)在编译时都会转化为 “无糖语法”。例如类委托:编译器会实现基础接口的所有方法,并直接委托给基础对象来处理。例如对象委托和局部变量委托:在编译时会生成辅助属性(prop$degelate),而属性 / 变量的 getter() 和 setter() 方法只是简单地委托给辅助属性的 getValue() 和 setValue() 处理。