kotlin中委托的概念和原理

时间:2023-02-08 17:00:28

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
}

代码提示报错,如下图所示: kotlin中委托的概念和原理 修改委托类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)
}

运行结果如下: kotlin中委托的概念和原理 有结果可知,第一次 println(p) 会打印 “00000”,而第二次不会。因为使用 lazy 方法将 request 的结果保存,第二次打印 data 时,直接返回 reqeust 结果,而不是再次执行 request。 查看lazy方法的源码如下: ....../kotlin/util/LazyJVM.kt:21: kotlin中委托的概念和原理 kotlin.SynchronizedLazyImpl: kotlin中委托的概念和原理 有源码可知,SynchronizedLazyImpl 实现了一个单例模式,如果 _value 初始化过,直接返回 _value 的值,否则使用 lazy 的入参函数 initializer 初始化,返回 T 类型的值,并赋值给 _value。

问题总结

本文对kotlin中的委托机制进行了一个初步的介绍,包括类委托、属性委托,同时对lazy延迟初始化关键字进行了一个简单的介绍,有兴趣的同学可以进一步深入学习。