前言
即,相同的事情,执行人从我 交给了 你。
我 不需要改动 我的部分,修改就交给你完成。增加了灵活性。
本节要关注几个问题。
① 委托是通过什么实现的?
② 实现委托有几个步骤,几个组成部分
③ 委托可以改变什么?属性?函数重载?
④ 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、继承的第二种实现、属性&函数的快捷修改、观察者模式的快捷实现。