使用场景
希望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<T>的扩展函数,operator关键字表明:by是运算符重载。访问b对象,就是调用Lazy<T>对象的getValue函数,而函数返回Lazy<T>对象的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对象,我们到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只执行一次,而且是线程安全的。
以上分析有不对的地方,请指出,互相学习,谢谢哦!
网友评论