浅谈Kotlin委托属性

概念

「委托属性」,从名字上,我们大概就能理解到它到底要干什么:第一,既然是「委托」,那免不了需要将相关功能委托给别人来帮忙完成;第二,委托的是属性,属性能委托什么呢?那自然就是其值的读写了。只要有这两点认识,我们就基本清楚了委托属性存在的意义。

简单说,「委托属性」就是将属性的读写委托给一个代理,由这个代理决定该属性的读写值,乃至读写行为。委托的发生,同样由关键词 by 触发,其句法为:

val/var <property name>: <Type> by <expression>

其中:

  • property name:属性名
  • Type:属性类型
  • expression:属性的委托表达式

属性的 get()set() 会被分别委托给两个方法: getValue()setValue(),这俩方法由 expression 提供。比如:

import kotlin.reflect.KProperty

class Delegate {
    // String类型的委托get
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return "$thisRef, thank you for delegating '${property.name}' to me!"
    }

    // String类型的委托set
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("$value has been assigned to '${property.name}' in $thisRef.")
    }
}

来看看效果:

var desc: String by Delegate()
fun main(args: Array<String>) {
    desc = "test"
    val r = desc
    println(r)
}

输出:

test has been assigned to 'desc' in null.
null, thank you for delegating 'desc' to me!

可变变量 desc 使用了 Delegate 委托。对 desc 赋值时,调用 setValue() 方法打印了相应的日志,value 是新值,thisRefdesc 引用。

desc 赋值给 r 时,会调用 getValue() 方法获取值,可以看到,这里还是 null,因为根本没有地方存储值,set 没有意义,值也无法保存。

值得注意的是,getValue()setValue() 前面都加了 operator,这说明,属性委托根本就是利用了赋值和读值的操作符重载技术!

对于 val 类型的属性,只需要 getValue() 即可。

标准库委托

标准库默认提供了一些十分实用的委托工具,其中就包括我们经常看到的「懒加载」委托 lazy

lazy

lazy 就是为「懒加载」设计的,它有两个要点:

  • 使用时初始化 —— 懒加载
  • 每次访问,使用初始化完成的已有值

怎么做的呢?

public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)

首先,lazy 其实就是一个工厂方法,返回一个 Lazy<T> 类型,而该类型对象由其实现类 SynchronizedLazyImpl 构造。来看看源码:

private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
    private var initializer: (() -> T)? = initializer
    @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
        // val类型变量,get()其值
        get() {
            val _v1 = _value
            // 只要不是默认值,就返回它
            if (_v1 !== UNINITIALIZED_VALUE) {
                @Suppress("UNCHECKED_CAST")
                return _v1 as T
            }
            
            // 同步进行初始化
            return synchronized(lock) {
                val _v2 = _value
                // 二次校验
                if (_v2 !== UNINITIALIZED_VALUE) {
                    @Suppress("UNCHECKED_CAST") (_v2 as T)
                } else {
                    // 未初始化,调用初始化工厂函数,获取T值
                    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)
}

public interface Lazy<out T> {
    /**
     * 懒加载实例,一次性构造
     */
    public val value: T

    /**
     * 标志是否初始化完成
     */
    public fun isInitialized(): Boolean
}

从上述代码可以看出,lazy 只能委托给 val 类型的变量,因为它只提供了 get()。也就是在 get()的逻辑中,它实现了「懒加载」和「一次性初始化」的功能。

lazy 还有非同步版本,这里不作深入探讨

Delegates

Delegates 是一个 object,它提供以下标准属性委托:

  • notNull:非空委托,可以让非空属性避免声明初始化
  • observable:可观察委托,每次赋值完成后,回调告知新旧值变化
  • vetoable:可拒绝委托,每次赋值生效前,判断一下条件是否满足

其中,可观察、可拒绝的委托,都是通过 ObservableProperty (可观察属性)来实现的。

public abstract class ObservableProperty<V>(initialValue: V) : ReadWriteProperty<Any?, V> {
    private var value = initialValue

    /**
     赋值前回调。其返回值,false:丢弃,true:继续赋值
     */
    protected open fun beforeChange(property: KProperty<*>, oldValue: V, newValue: V): Boolean = true

    /**
     赋值完成后回调
     */
    protected open fun afterChange(property: KProperty<*>, oldValue: V, newValue: V): Unit {}

    public override fun getValue(thisRef: Any?, property: KProperty<*>): V {
        return value
    }

    public override fun setValue(thisRef: Any?, property: KProperty<*>, value: V) {
        val oldValue = this.value
        // 这里先回调before,如果返回false,就return丢弃了,值不更新
        if (!beforeChange(property, oldValue, value)) {
            return
        }
        // 走到这里,证明可以更新,更新并回调after
        this.value = value
        afterChange(property, oldValue, value)
    }
}

源码很简单。而 observablevetoable 就分别覆写了 afterChangebeforeChange 来达到「观察」、「拒绝」的效果:

public inline fun <T> observable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit):
            ReadWriteProperty<Any?, T> =
        object : ObservableProperty<T>(initialValue) {
            // 通过 onChange() -> Unit 传递回调
            override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = onChange(property, oldValue, newValue)
        }
        
public inline fun <T> vetoable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Boolean):
            ReadWriteProperty<Any?, T> =
        object : ObservableProperty<T>(initialValue) {
            // 通过onChange() -> Boolean 传递回调
            override fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = onChange(property, oldValue, newValue)
        }

其中,vetoableonChange 是需要返回布尔值的,用于判断待赋的值,抛弃与否。

小结

今天算是对委托属性有了一个基本认识,也学习了下标准库里常用的委托属性工具的使用及其原理。一句话总结,委托属性就是为属性的读写服务的

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集

在这里插入图片描述
二、源码解析合集
在这里插入图片描述

三、开源框架合集
在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值