zoukankan      html  css  js  c++  java
  • Kotlin 委托

    什么是委托?

    把自己的事情托付给别人来办理。

    常见的代理模式就是如此,被代理类接受代理类的委托来处理代理类的事情。

    这里不谈设计模式,只记录一下 kotlin 中的 类委托和属性委托。

    • 属性委托:把属性的 setter 和 getter 委托给另一个类处理
    • 类委托:把自己事情委托给另一个类处理

    为什么需要委托?

    属性委托可以使用另一个类的属性来替换当前属性的 getter 和 setter 。

    类委托可以使用组合代替继承。

    kotlin 在语法层面对委托进行了支持,从而减少了很多的样板代码

    用在什么场景下?

    属性委托

    在你想把属性的读取逻辑放在另一个地方的时候就可以用到了,比如你想更改一个属性名字,而这个库已经发出去了,删除是不可能的,只能废弃,这时候就可以使用委托,将这个旧属性委托给新属性。

    还有 标准库里提供的几个场景

    1. 延迟初始化;属性在第一次使用时才会初始化,而延迟逻辑就放在了类的外面,委托给了另一个类处理
    2. 可观察属性;可以观察属性的改变,一旦有变化可以得到通知,这些观察的逻辑实现也委托到了另外的类处理
    3. 把多个属性储存在一个映射(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 核心编程》
  • 相关阅读:
    创建本地源,使用yum install
    查找SCAN大量块的一个sql
    好的代码像首诗,差的代码像坨屎。
    ps
    eclipse程序正确运行却有红叉
    JS中文乱码解决方案
    初学JQuery
    初学JQuery 2
    大神的电脑软件
    eclipse导入已存在于workspace的项目
  • 原文地址:https://www.cnblogs.com/skymxc/p/kotlin-delegate.html
Copyright © 2011-2022 走看看