美文网首页
kotlin-委托

kotlin-委托

作者: Method | 来源:发表于2021-10-29 16:45 被阅读0次

    [toc]

    委托是什么

    委托又可以称为代理。为其他对象提供一种代理以控制对这个对象的访问,简单的说就是在访问和被访问对象中间加上的一个间接层,以隔离访问者和被访问者的实现细节。

    类委托

    类委托其实对应于Java中的代理模式

    interface Base{
        fun text()
    }
    
    //被委托的类(真实的类)
    class BaseImpl(val x :String ) : Base{
        override fun text() {
            println(x)
        }
    }
    
    //委托类(代理类)
    class Devices (b :Base) :Base by b
    
    
    fun main(){
        var b = BaseImpl("我是真实的类")
        Devices(b).text()
    }
    
    

    输出

    我是真实的类

    可以看到,委托类(代理类)持有真实类的对象,然后委托类(代理类)调用真实类的同名方法,最终真正实现的是方法的是真实类,这其实就是代理模式

    kotlin中委托实现借助于by关键字,by关键字后面就是被委托类,可以是一个表达式

    接口委托

    如果接口有很多方法,子类就必须全部实现。

    interface Api{
        fun a()
        fun b()
        fun c()
    }
    
    class ApiWrapper:Api{
        override fun a() {
        }
    
        override fun b() {
        }
    
        override fun c() {
        }
    }
    

    kotlin可以找个对象代替你实现它,我们只需要实现需要的方法即可。

    class ApiWrapper(val api:Api):Api by api{
        override fun a() {
            println("判断是否登录")
            api.a()
        }
    }
    

    设计一个好玩的例子

    class SupperArray<T>(val list: MutableList<T> = mutableListOf(), val map: MutableMap<String, T> = mutableMapOf()) :
        MutableList<T> by list, MutableMap<String, T> by map{
    
        override fun clear() {
            list.clear()
            map.clear()
        }
    
        override fun isEmpty(): Boolean {
            return list.isEmpty() && map.isEmpty()
        }
    
        override val size: Int
            get() = list.size+map.size
    
        override fun toString(): String {
            return "$list  $map"
        }
    }
    
    

    这是一个超级集合,包含一个list和一个map。这里用了代理,不需要实现全部的方法。只需要提供必要的方法。

    fun main(args: Array<String>) {
        val supperArray = SupperArray<String>()
        supperArray["a"]= "hello"
        supperArray.add("world")
        println(supperArray.toString())
    }
    

    打印

    [world]  {a=hello}
    

    属性委托

    属性委托和类委托一样,属性的委托其实是对属性的==set/get==方法的委托,把set/get方法委托给==setValue/getValue==方法,因此被委托类(真实类)需要提供==setValue/getValue==方法,val属性只需要提供==setValue==方法

    属性委托语法:

    val/var <属性名>: <类型> by <表达式>

    
    class B {
        //委托属性
        var a : String by Text()
    }
    
    //被委托类(真实类)
    class Text {
        operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
            return "属性拥有者 = $thisRef, 属性的名字 = '${property.name}' 属性的值 "
        }
    
        operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
            println("属性的值 = $value 属性的名字 =  '${property.name}' 属性拥有者 =  $thisRef")
        }
    }
    
    fun main(){
        var b = B()
    
        println(b.a)
    
        b.a = "ahaha"
        
    }
    

    输出

    属性拥有者 = com.example.lib.weituo.B@27fa135a, 属性的名字 = 'a' 属性的值 
    属性的值 = ahaha 属性的名字 =  'a' 属性拥有者 =  com.example.lib.weituo.B@27fa135a
    

    上面的例子可以看到 ,属性a 委托给了Text,而且Text类中有setValue和getValue,所以当我们调用属性a的get/set方法时候,会委托到Text的setValue/getValue上,上面的例子可以看出来,里面有几个参数介绍一下:

    thisRef:属性的拥有者
    property:对属性的描述,是KProperty<*>类型或者父类
    value:属性的值
    

    更加简单的实现属性委托

    每次实现委托都要写getValue/setValue方法,这就比较麻烦了,系统为我们提供了接口,方便我们重写这些方法,ReadOnlyProperty和ReadWriteProperty

    interface ReadOnlyProperty<in R, out T> {
        operator fun getValue(thisRef: R, property: KProperty<*>): T
    }
    
    interface ReadWriteProperty<in R, T> {
        operator fun getValue(thisRef: R, property: KProperty<*>): T
        operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
    }
    

    Koltin 标准库中提供的几个委托

    延迟属性(lazy properties):其值只在访问时初始化,再次调用直接返回结果
    可观察属性(observable properties):监听器会收到此属性的变更通知
    把多个属性映射到(Map)中,而不是存在单个字段
    

    延迟属性lazy

    lazy()接收一个lambda,返回Lazy实例,返回的实例可以作为实现延迟属性的委托,仅在第一次调用属性进行初始化

    class Lazy {
        val name :String by lazy {
            println("第一次调用初始化")
            "aa" }
    }
    
    fun main(){
        var lazy =Lazy()
        println(lazy.name)
        println(lazy.name)
        println(lazy.name)
    }
    
    

    输出

    第一次调用初始化
    aa
    aa
    aa
    

    lazy委托参数

    SYNCHRONIZED:添加同步锁,使lazy延迟初始化线程安全
    PUBLICATION:初始化的lambda表达式,可以在同一时间多次调用,但是只有第一次的返回值作为初始化值
    NONE:没有同步锁,非线程安全
    
    1. by lazy是通过属性代理来实现的懒加载,只在第一次使用的时候才会去执行表达式,并且只会执行一次。
    2. by lazy默认是线程安全的,内部通过双重判断锁来保证只执行一次代码块赋值
    3. 当能够确定不会发生在多线程中的时候,可通过lazy(LazyThreadSafetyMode.NONE) { ... }来避免加锁。
    

    lazy 原理

    通过前面对属性委托的简单介绍,我们也明白了属性委托的机制。by后面跟着的肯定是一个对象,也就是委托对象,该对象负责属性的get/set,所以lazy这个方法最终肯定会返回一个对象。

    public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
    
    public actual fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
        when (mode) {
            LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
            LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
            LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
        }
        
    public actual fun <T> lazy(lock: Any?, initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer, lock)
    
    

    lazy方法一共有三个,我们最常用的是第一个。从代码中可以看出,lazy方法最终是创建的Lazy<T>的实例,这个实例也就是属性委托的对象。

    public interface Lazy<out T> {
        public val value: T
        public fun isInitialized(): Boolean
    }
    
    

    Lazy一共有三个子类,其中我们使用的第一个方法返回的是SynchronizedLazyImpl,这是Lazy的其中一个实现。

    internal object UNINITIALIZED_VALUE
    
    private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
        private var initializer: (() -> T)? = initializer
        
        // 委托的属性的值由_value记录,初始值是单例对象UNINITIALIZED_VALUE
        @Volatile private var _value: Any? = UNINITIALIZED_VALUE
        // final field is required to enable safe publication of constructed instance
        private val lock = lock ?: this
    
        override val value: T
            get() {
                val _v1 = _value
                // 已初始化则直接返回对应值
                if (_v1 !== UNINITIALIZED_VALUE) {
                    @Suppress("UNCHECKED_CAST")
                    return _v1 as T
                }
                
                // 未初始化的,执行传递进来的lambda参数进行赋值
                return synchronized(lock) {
                    val _v2 = _value
                    if (_v2 !== UNINITIALIZED_VALUE) {
                        @Suppress("UNCHECKED_CAST") (_v2 as T)
                    } else {
                        val typedValue = initializer!!()
                        _value = typedValue
                        initializer = null
                        typedValue
                    }
                }
            }
    
        override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
    
        override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
    
        private fun writeReplace(): Any = InitializedLazyImpl(value)
    }
    
    

    SynchronizedLazyImpl是Lazy的一个子类,可以看到在value属性的get方法中,会去判断是否已经初始化过,若是没有初始化,则会调用initializer去进行初始化,赋值给_value。若是已经初始化过,则会直接返回 _value的值。因此by lazy就能通过这种方式去实现懒加载,并且只加载一次。

    从代码中可以看出,SynchronizedLazyImpl是保证了线程安全的。是通过DCL方式来保证的安全,能够确保在多线程下也只会执行一次代码块。

    但是我们说过,要实现可读属性委托,必须实现getValue方法。而在SynchronizedLazyImpl中,并没有实现getValue方法,而是只有value属性的get方法。这里猜测编译器应该是对Lazy有特殊的处理,而通过实验,实现了Lazy接口的对象确实可以作为只读属性的委托对象。而其他接口即使与Lazy一模一样,实现它的对象也不能作为属性委托对象。

    可观察属性Observable委托

    可以观察一个属性的变化过程

    class Lazy {
       
        var a : String by Delegates.observable("默认值"){
                property, oldValue, newValue ->
    
            println( "$oldValue -> $newValue ")
        }
    }
    
    fun main(){
        var lazy =Lazy()
    
    
        lazy.a = "第一次修改"
    
        lazy.a = "第二次修改"
    }
    
    

    输出

    默认值 -> 第一次修改 
    第一次修改 -> 第二次修改
    

    vetoable委托

    vetoable和Observable一样,可以观察属性的变化,不同的是vetoable可以决定是否使用新值

    class C {
        var age: Int by Delegates.vetoable(0) { property, oldValue, newValue ->
            println("oldValue = $oldValue -> oldValue = $newValue" )
            newValue > oldValue
    
    
        }
    }
    
    fun main() {
        var c = C()
    
        c.age = 5
        println(c.age)
    
        c.age = 10
        println(c.age)
    
        c.age = 8
        println(c.age)
    
        c.age = 20
        println(c.age)
    }
    
    

    输出

    oldValue = 0 -> oldValue = 5
    5
    oldValue = 5 -> oldValue = 10
    10
    oldValue = 10 -> oldValue = 8
    10
    oldValue = 10 -> oldValue = 20
    20
    

    当新值小于旧值,那么就不生效,可以看到第三次设的值是8,小于10就没有生效

    属性储存在Map中

    class D(val map: Map<String, Any?>) {
        val name: String by map
        val age: Int by map
    }
    
    fun main(){
        var d = D(mapOf(
            "name" to "小明",
            "age" to 12
        ))
    
        println("name = ${d.name},age = ${d.age}")
    }
    
    

    输出

    name = 小明,age = 12

    相关文章

      网友评论

          本文标题:kotlin-委托

          本文链接:https://www.haomeiwen.com/subject/dunialtx.html