Kotlin-委托剖析(3)-by lazy

876 阅读2分钟
使用场景

希望b对象只构建一次,那么就可以用到by lazy 委托。

class MyApplication : Application() {
    val b by lazy { B() }
}

那么它是如何保证对象只构建一次呢?先看下源码

by关键字源码
@kotlin.internal.InlineOnly
public inline operator fun <T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value

这个是Lazy的扩展函数,operator关键字表明:by是运算符重载。访问b对象,就是调用Lazy对象的getValue函数,而函数返回Lazy对象的value值。

lazy关键字源码
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)

返回一个SynchronizedLazyImpl对象,包含lambda表达式,也就是上述示例代码中的 { B() }

初步判断:外部访问b对象,也就是触发SynchronizedLazyImpl的getValue函数;

//伪代码
class MyApplication : Application() {
    val b = SynchronizedLazyImpl().getValue()
}
再反编译看看
public final class MyApplication extends Application {
   @NotNull
   private final Lazy b$delegate;//1

   @NotNull
   public final B getB() {//2
      Lazy var1 = this.b$delegate;
      Object var3 = null;
      boolean var4 = false;
      return (B)var1.getValue();
   }

   public MyApplication() {
      this.b$delegate = LazyKt.lazy((Function0)null.INSTANCE);//3
   }
}
  • 注释1:类中多了一个b$delegate对象(具体类型是SynchronizedLazyImpl),会在MyApplication 实例化的时候构建,具体看注释3;
  • 注释2:外部调用b,其实是触发getB(),最后一行代码,具体的实现都转移到Lazy对象当中,这是一个委托设计模式;
  • 注释3:MyApplication 初始化的时候,构建bdelegate对象,“(Function0)null.INSTANCE”,这里我理解为把lambda表达式参数传入,但为什么是null.INSTANCE,暂时无法理解,哪位大神可以帮忙解释下。通过以上分析得到,getB()的逻辑都委托给了bdelegate对象,“(Function0)null.INSTANCE”,这里我理解为把lambda表达式参数传入,但为什么是null.INSTANCE,暂时无法理解,哪位大神可以帮忙解释下。 通过以上分析得到,getB()的逻辑都委托给了bdelegate对象,我们到SynchronizedLazyImpl去看下
private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {//1
    private var initializer: (() -> T)? = initializer
    @Volatile private var _value: Any? = UNINITIALIZED_VALUE
    private val lock = lock ?: this

    override val value: T
        get() {//2
            val _v1 = _value
            if (_v1 !== UNINITIALIZED_VALUE) {//3
                @Suppress("UNCHECKED_CAST")
                return _v1 as T
            }

            return synchronized(lock) {//4
                val _v2 = _value
                if (_v2 !== UNINITIALIZED_VALUE) {//5
                    @Suppress("UNCHECKED_CAST") (_v2 as T)
                } else {
                    val typedValue = initializer!!()//6
                    _value = typedValue
                    initializer = null
                    typedValue//7
                }
            }
        }
......
}
  • 注释1:从名字上就知道这个类是线程安全的,入参为函数对象,这里也印证了把lambda表达式作为参数传入;
  • 注释2:外部调用的getB()函数就会转移到这里;
  • 注释3:_value的默认值是UNINITIALIZED_VALUE,意思没有初始化;
  • 注释4:加锁,这里就是线程安全处理;
  • 注释5:再次判断是否已经初始化,双重判断;
  • 注释6:执行lambda表达式,结果赋值给_value ,那么下次就不会再执行lambda了,也就保证只初始化一次;
  • 注释7,返回结果。
总结

by lazy 具体实现转移给SynchronizedLazyImpl进行处理,其保证了lamdbda只执行一次,而且是线程安全的。

以上分析有不对的地方,请指出,互相学习,谢谢哦!