kotlin中委托的概念和原理

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了kotlin中委托的概念和原理相关的知识,希望对你有一定的参考价值。

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延迟初始化关键字进行了一个简单的介绍,有兴趣的同学可以进一步深入学习。

以上是关于kotlin中委托的概念和原理的主要内容,如果未能解决你的问题,请参考以下文章

细说委托

.Net中委托的协变和逆变详解

关于C#中委托的多播应用问题

c#中委托的作用是啥?

深入理解委托——为什么C#要引入委托

kotlin中闭包的概念和原理