kotlin中委托的概念和原理
问题背景
kotlin的日常使用过程中,经常会使用到委托机制,问题来了,委托机制究竟是什么呢? 委托模式:多个对象接收并处理同一请求,他们将请求委托给另一个对象统一处理请求。 比如调用A类的methodA方法,其实背后是B类的methodB去执行。
问题分析
Kotlin 的委托机制在语言层面自动实现了类似 Java 的组合代理。Kotlin 的委托包括委托类、委托属性,使用 by 关键字表示委托。
1、类委托
(1)假设有一个 Db接口和一个GreenDaoDb类,用来保存数据。代码如下:
interface Db {
fun save()
}
GreenDaoDb类
class GreenDaoDb : Db {
override fun save() {
println("green dao db save()")
}
}
(2)如果使用 Java 风格的委托方式,可能会这么写,代码如下:
class MyDb(private val db: Db) : Db {
override fun save() {
db.save()
}
}
代码分析: 这种方式有很多样板代码,比如重写 save(),在接口方法调用属性 db 的 save(),类似于很多时候java的代理模式使用的样板代码。 (3)上面的java风格的委托方式,如果使用kotlin的委托机制,可以怎么实现呢?代码如下:
class UniversalDb(db: Db) : Db by db
Db 接口的所有方法都交给 by 关键字后面的 db 实现。 另外,如果我们要对某个方法进行重新实现或者新增,只需要单独重写那一个方法就可以了,其他的方法仍然可以享受类委托所带来的便利,如下所示:
class UniversalDb(db: Db) : Db by db {
fun helloWorld() = println("Hello World")
}
由上面可以看出,类委托的核心思想是将一个类的具体实现委托给另一个类去完成。
2、委托属性
对应的,委托属性的核心思想是将一个属性(字段)的具体实现委托给另一个类去完成。 委托属性的语法结构,代码如下:
class MyTest {
// 属性代理的语法结构
var p by Delegate()
}
实现Delegate类,代码如下:
class MyClass {
var p by Delegate()
}
class Delegate {
var propValue: Any? = null
}
代码提示报错,如下图所示: 修改委托类Delegate代码如下:
import kotlin.reflect.KProperty
class MyTest {
var p by Delegate()
}
class Delegate {
var propValue: Any? = null
operator fun getValue(myClass: MyTest, prop: KProperty<*>): Any? {
return propValue
}
operator fun setValue(myClass: MyTest, prop: KProperty<*>, value: Any?) {
propValue = value
}
}
分析可知,在Delegate类中我们必须实现getValue()和setValue()这 两个方法,并且都要使用operator关键字进行声明。 getValue()方法要接收两个参数:第一个参数用于声明该Delegate类的委托功能可以在什么类中使用,这里写成MyTest表示仅可在MyTest类中使用;第二个参数KProperty<*>是 Kotlin中的一个属性操作类,可用于获取各种属性相关的值,在当前场景下用不着,但是必须在方法参数上进行声明。
setValue()方法也是相似的,只不过它要接收3个参数。前两个参数和getValue()方法是相 同的,最后一个参数表示具体要赋值给委托属性的值,这个参数的类型必须和getValue()方法返回值的类型保持一致。
3、懒加载委托
Kotlin通过by关键字就可以实现委托的效果,比如的by lazy { },其实就是利用委托实现的延迟初始化语法。 以下是它的使用:
val p by lazy {
print("12345")
123456
}
这里使用了一种懒加载技术,把想要延迟执行的代码放到by lazy代码块中,这样代码块中的代码在一开始的时候就不会执行,只有当laziness变量首次被调用的时候,代码块中的代码才会执行,代码如下:
val p by lazy {
println("00000")
123456
}
fun main() {
// lazy delegate
println("lazy delegate beging")
println(p)
println(p)
}
运行结果如下: 有结果可知,第一次 println(p) 会打印 “00000”,而第二次不会。因为使用 lazy 方法将 request 的结果保存,第二次打印 data 时,直接返回 reqeust 结果,而不是再次执行 request。 查看lazy方法的源码如下: ....../kotlin/util/LazyJVM.kt:21: kotlin.SynchronizedLazyImpl: 有源码可知,SynchronizedLazyImpl 实现了一个单例模式,如果 _value 初始化过,直接返回 _value 的值,否则使用 lazy 的入参函数 initializer 初始化,返回 T 类型的值,并赋值给 _value。
问题总结
本文对kotlin中的委托机制进行了一个初步的介绍,包括类委托、属性委托,同时对lazy延迟初始化关键字进行了一个简单的介绍,有兴趣的同学可以进一步深入学习。