类委托

类委托实现原理简单,就是普通的代理模式。

  1. 实现一个统一的接口
  2. 接收一个真实的接口实现对象为参数
  3. 通过关键字 by 来省略接口方法的覆写

一个简单的例子

接口

  1. interface Defence {
  2. /**
  3. * 防御攻击
  4. * @param attackAmount 攻击次数
  5. */
  6. fun defenceAttack(attackAmount: Int)
  7. }

实现类

  1. class BaseDefence : Defence {
  2. val declaration = "最好的攻击就是防御!!"
  3. fun showDeclaration() {
  4. println(declaration)
  5. }
  6. override fun defenceAttack(attackAmount: Int) {
  7. println("${this.javaClass.simpleName} 我挡住了${attackAmount}次攻击")
  8. }
  9. }

委托类

  1. class MyDefence(impl: Defence) : Defence by impl {
  2. }

调用

  1. fun main() {
  2. val baseDefence = BaseDefence()
  3. val myDefence = MyDefence(baseDefence)
  4. myDefence.defenceAttack(22)
  5. }

打印结果:

BaseDefence 我挡住了22次攻击

匿名类委托

  1. // 匿名类,也可以使用类委托
  2. val d = object : Defence by baseDefence {}
  3. d.defenceAttack(2)

BaseDefence 我挡住了2次攻击

实现原理

实现原理很简单,反编译MyDefence类即可以明白

  1. public final class MyDefence implements Defence {
  2. // $FF: synthetic field
  3. private final Defence $$delegate_0;
  4. public void showDeclaration() {
  5. String var1 = "我的防御99999";
  6. boolean var2 = false;
  7. System.out.println(var1);
  8. }
  9. public MyDefence(@NotNull Defence impl) {
  10. Intrinsics.checkNotNullParameter(impl, "impl");
  11. super();
  12. this.$$delegate_0 = impl;
  13. }
  14. public void defenceAttack(int attackAmount) {
  15. this.$$delegate_0.defenceAttack(attackAmount);
  16. }
  17. }

结论:

  1. 与代理模式一模一样,都是通过构造函数传入具体实现对象
  2. 自动生成代理接口中的所有方法,并且由构造函数传入的实现类调用
  3. 方法可以被覆盖,覆盖的方法由本类调用

委托属性

这是kotlin中非常关键的一个特征,by lazy{}等都是通过此来实现的

有一些常见的属性类型,虽然我们可以在每次需要的时候手动实现它们, 但是如果能够为大家把他们只实现一次并放入一个库会更好。例如包括:

  • 延迟属性(lazy properties): 其值只在首次访问时计算;
  • 可观察属性(observable properties): 监听器会收到有关此属性变更的通知;
  • 把多个属性储存在一个映射(map)中,而不是每个存在单独的字段中。

基本用法

被委托的类需要实现2个接口

  1. // 只读接口,对应 val
  2. public fun interface ReadOnlyProperty<in T, out V> {
  3. public operator fun getValue(thisRef: T, property: KProperty<*>): V
  4. }
  1. // 读写接口,继承了只读接口,对应 var
  2. public interface ReadWriteProperty<in T, V> : ReadOnlyProperty<T, V> {
  3. public override operator fun getValue(thisRef: T, property: KProperty<*>): V
  4. public operator fun setValue(thisRef: T, property: KProperty<*>, value: V)
  5. }

实现接口的被委托类

  1. class FirstDelegate : ReadWriteProperty<Any?, Int>{
  2. private var invokeGetAmount = 0
  3. private var invokeSetAmount = 0
  4. override fun getValue(thisRef: Any?, property: KProperty<*>): Int {
  5. invokeGetAmount++
  6. println("对象:'${thisRef?.javaClass?.simpleName}'中的'${property.name}'获取了一次值,返回数值:${invokeGetAmount}")
  7. return invokeGetAmount
  8. }
  9. override fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
  10. invokeSetAmount++
  11. println("对象:'${thisRef?.javaClass?.simpleName}'中的'${property.name}'赋值一次${value},调用次数${invokeSetAmount}")
  12. }
  13. }

赋值委托

  1. // 使用委托属性
  2. var d1 : Int by FirstDelegate()
  3. // 根据被委托的类getValue、setValue自动推断
  4. var d2 by FirstDelegate()
  5. // 自动推断类型 与 当前定义的String类型不符合,报错
  6. //var d3 : String by FirstDelegate()
  7. fun main() {
  8. d1 = 22
  9. println(d1)
  10. println(d1)
  11. }

对象:’null’中的’d1’赋值一次22,调用次数1 对象:’null’中的’d1’获取了一次值,返回数值:1 1 对象:’null’中的’d1’获取了一次值,返回数值:2 2

结论:

  1. 委托属性使用关键字 by ,并且不用实现接口
  2. 被委托类需要实现接口 ReadOnlyProperty 或者 ReadWriteProperty
  3. 委托泛型T,委托属性当前引用所在的空间,即thisRef,如果空间不符合被委托类的定义范围,则不能委托(比如顶层属性所在的空间为null,如果thisRef类型为Any,则不能接顶层属性的委托,必须为Any?才行)
  4. 委托泛型V,get()、set()方法的类型,必须保持一致。委托属性必须是该类型V才能委托给该类

operator关键字高级写法

  1. class SecondDelegate {
  2. private var getValueAmount = 0
  3. private var setValueAmount = 0
  4. // operator 关键字就可以告诉编译器这是覆写了某个操作,不再需要继承接口
  5. operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
  6. val str = "第${++getValueAmount}次读取该属性数值"
  7. println("对象:${thisRef?.javaClass?.simpleName} 中的 ${property.name} 获取了一次值,返回值:${str}")
  8. return str
  9. }
  10. operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
  11. val str = "第${++setValueAmount}次赋值->$value"
  12. println("对象:${thisRef?.javaClass?.simpleName} 中的 ${property.name} 赋值一次:${str}")
  13. }
  14. }

作用

单独看委托属性,就是把某个属性的get(),set()方法单独扔出去到外部实现,作用并不是太大。
但是结合泛型+高级函数,就可以实现一类型的操作封装,也正是委托属性的关键意义

by lazy{} 分析

val str by lazy { "abc" }
by lazy{} 可以实现通用的延迟初始化操作,类似懒汉式的单例,并且不需要自己写代码实现,非常实用,分析其实现过程就可以了解属性委托的作用

源码:

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

lazy方法接收一个高级函数,initializer: () -> T� 返回值Lazy<T>,具体实现过程先不管,看看返回值是什么

  1. public interface Lazy<out T> {
  2. /**
  3. * Gets the lazily initialized value of the current Lazy instance.
  4. * Once the value was initialized it must not change during the rest of lifetime of this Lazy instance.
  5. */
  6. public val value: T
  7. /**
  8. * Returns `true` if a value for this Lazy instance has been already initialized, and `false` otherwise.
  9. * Once this function has returned `true` it stays `true` for the rest of lifetime of this Lazy instance.
  10. */
  11. public fun isInitialized(): Boolean
  12. }

这是一个接口,没有getValue()方法,那怎么能被委托呢??,继续往下找

  1. @kotlin.internal.InlineOnly
  2. public inline operator fun <T> Lazy<T>.getValue(
  3. thisRef: Any?,
  4. property: KProperty<*>)
  5. : T = value

委托函数getValue()是通过扩展函数实现的,这种方式也能够实现委托!
返回值 : T = value,直接返回了接口中的 public val value: T
那么只需要查看接口的实现类SynchronizedLazyImpl(initializer)中value赋值的地方即可

  1. private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
  2. private var initializer: (() -> T)? = initializer
  3. @Volatile private var _value: Any? = UNINITIALIZED_VALUE
  4. // final field is required to enable safe publication of constructed instance
  5. private val lock = lock ?: this
  6. override val value: T
  7. get() {
  8. val _v1 = _value
  9. if (_v1 !== UNINITIALIZED_VALUE) {
  10. @Suppress("UNCHECKED_CAST")
  11. return _v1 as T
  12. }
  13. return synchronized(lock) {
  14. val _v2 = _value
  15. if (_v2 !== UNINITIALIZED_VALUE) {
  16. @Suppress("UNCHECKED_CAST") (_v2 as T)
  17. } else {
  18. val typedValue = initializer!!()
  19. _value = typedValue
  20. initializer = null
  21. typedValue
  22. }
  23. }
  24. }
  25. override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
  26. override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
  27. private fun writeReplace(): Any = InitializedLazyImpl(value)
  28. }

实现比较简单,就是通过双重判空的锁实现,与单例模式一样

总结

委托属性的作用,一般都是与泛型+高级函数一起使用,这样才能把同一类型的操作简化为一个by关键字+委托类实现
自己需要用到属性委托的地方都是为了解决一类问题,而不是单个问题,遇到的时候再补充