概念
「委托属性」,从名字上,我们大概就能理解到它到底要干什么:第一,既然是「委托」,那免不了需要将相关功能委托给别人来帮忙完成;第二,委托的是属性,属性能委托什么呢?那自然就是其值的读写了。只要有这两点认识,我们就基本清楚了委托属性存在的意义。
简单说,「委托属性」就是将属性的读写委托给一个代理,由这个代理决定该属性的读写值,乃至读写行为。委托的发生,同样由关键词 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
是新值,thisRef
是 desc
引用。
将 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)
}
}
源码很简单。而 observable
和 vetoable
就分别覆写了 afterChange
和 beforeChange
来达到「观察」、「拒绝」的效果:
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)
}
其中,vetoable
的 onChange
是需要返回布尔值的,用于判断待赋的值,抛弃与否。
小结
今天算是对委托属性有了一个基本认识,也学习了下标准库里常用的委托属性工具的使用及其原理。一句话总结,委托属性就是为属性的读写服务的。
最后
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
全套视频资料:
一、面试合集
二、源码解析合集
三、开源框架合集
欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓