Kotlin 委托
什么是委托?
把自己的事情托付给别人来办理。
常见的代理模式就是如此,被代理类接受代理类的委托来处理代理类的事情。
这里不谈设计模式,只记录一下 kotlin 中的 类委托和属性委托。
- 属性委托:把属性的 setter 和 getter 委托给另一个类处理
- 类委托:把自己事情委托给另一个类处理
为什么需要委托?
属性委托可以使用另一个类的属性来替换当前属性的 getter 和 setter 。
类委托可以使用组合代替继承。
kotlin 在语法层面对委托进行了支持,从而减少了很多的样板代码
用在什么场景下?
属性委托
在你想把属性的读取逻辑放在另一个地方的时候就可以用到了,比如你想更改一个属性名字,而这个库已经发出去了,删除是不可能的,只能废弃,这时候就可以使用委托,将这个旧属性委托给新属性。
还有 标准库里提供的几个场景
- 延迟初始化;属性在第一次使用时才会初始化,而延迟逻辑就放在了类的外面,委托给了另一个类处理
- 可观察属性;可以观察属性的改变,一旦有变化可以得到通知,这些观察的逻辑实现也委托到了另外的类处理
- 把多个属性储存在一个映射(map)中,而不是每个存在单独的字段中。
类委托
委托模式已经证明是实现继承的一个很好的替代方式。
怎么使用?
kotlin 在语法层面支持了委托,编译器会帮助我们生成样板代码。
委托属性
语法:
val/var <属性名>: <类型> by <表达式>。by 后面的表达式就是委托。
class Example {
var p: String by Delegate()
}
标准库里的 属性延迟初始化委托和可观察委托使用
val s:String by lazy {
"Hello s."
}
val name :String by Delegates.observable("佛系编码"){property,oldValue,newValue ->
println("${property.name} 的值即将发生变化,新值:${newValue},旧值:${newValue}")
}
标准库中能提供了 延迟属性委托的工厂方法 lazy ,传入一个初始化值的 lambda 就能使用了。
只有在没有被赋值时才会被调用一次并将 lambda的返回值赋值存储,成功赋值后就会直接使用值了,不会再调用 lambda 表达式。如果出现赋值错误下次会再次调用直到成功赋值为止。
lazy 还有一个参数是可选的 mode ,
val s:String by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
"Hello s."
}
该参数接受一个枚举类型 LazyThreadSafetyMode,目前有三个枚举值
- LazyThreadSafetyMode.SYNCHRONIZED 锁定并确定只有一个线程可初始化 Lazy 实例;默认值。
- LazyThreadSafetyMode.PUBLICATION 初始化函数可以调用多次,但只有第一次返回的值被使用。
- LazyThreadSafetyMode.NONE 无锁
标准库里除了 lazy 委托,在 Delegates 类里还有很多其他委托,就不一一的列举了,详情可以自己看看源码或者 《Kotlin编程实践》 第八章。
类委托
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(b: Base) : Base by b
fun main() {
val b = BaseImpl(10)
Derived(b).print()
}
如何自定义属性委托
只需要委托类提供 getValue 和 setValue (对应 var 变量)函数 即可,不需要实现任何接口。
标准的函数签名如下
import kotlin.reflect.KProperty
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$value has been assigned to '${property.name}' in $thisRef.")
}
}
标准库中也提供了接口 ReadOnlyProperty 和 ReadWriteProperty 分别对应 val 和 var ,觉得函数签名不好记得话,直接实现这个接口就好了。
示例
class Example{
var p :String by MyDelegate()
val s:String by lazy {
"Hello s."
}
}
class MyDelegate:ReadWriteProperty<Example,String>{
override fun setValue(thisRef: Example, property: KProperty<*>, value: String) {
println("MyDelegate 的 setValue 被调用 thisRef:$thisRef , property:${property.name} , value:$value")
}
override fun getValue(thisRef: Example, property: KProperty<*>): String {
return "MyDelegate 的 getValue 被调用 thisRef :$thisRef,property:${property.name}"
}
}
kotlin 的委托如何实现的?
kotlin 中的委托是由编译器将代码生成了,编译为 Java 代码就能看到。
属性委托,是将本来被委托的属性的 getter 和 setter 交由委托类的 getValue 和 setValue 处理了。
而类委托更简单了,内部完全是调用了委托的实现。
直接上代码更加直观一些,将上面的示例编译为 Java 代码可看到如下代码
被委托属性 p 只有 setter 和 getter 方法了。本身这个属性就不存在于类成员里了。
学习资料
- 委托属性
- 《Kotlin 编程实践》
- 《Kotlin 核心编程》