这篇文章,纯属较真。说它是八股文吧,不会问这么偏,说不是吧,但涉及到
by和lazy。而且,通常情况下,使用的都是by lazy而非= lazy。
在刷聊天群的时候,突然群里有人问出by lazy和= lazy有什么区别。
想一想,by lazy是赋值操作,= lazy 也应该算是赋值操作,这两者可能没有区别吧。
但如果真的是这样的话,就没必要有这篇文章了。
code
直接上代码
private val a by lazy { "a" }
private val b = lazy { "b" }
...省略代码...
Log.e("TAG", a)
Log.e("TAG", b.value)
区别
1、a的类型是String,而b的类型是Lazy。想要打印对象b的值,得用b.value
by lazy是延迟初始化,只有在首次访问的时候才将a赋值给对象a,而=lazy是在属性赋值时立即执行赋值,将Lazy<String>赋值给对象b。
3、对象a只能用val来修饰,但对象b不仅可以val, 还可以var。
初始化
但,但是,这里对对象b的输出是v.value,假如使用b.toString()的话,就又是另一种情况了。
//打印
Log.e("TAG", b.toString())
//结果
E/TAG: Lazy value not initialized yet.
奇怪了,为什么是lazy的值尚未初始化?
进入源码会发现,lazy在LazyJVM.kt文件中原来是这么实现的,
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
把函数作为参数,传值,最后关键的是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
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 {
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)
}
在这个类中,lazy的value在初始化时默认是Any类型,值为UNINITIALIZED_VALUE,而其value是只读类型,只实现了get方法,在调用get的时候,先判断是否初始化,如果已经初始化了,直接返回_value的值,否则对_value进行赋值。
而这个类又实现了toString()方法,假如已经初始化了,就返回值,如果没有初始化,就返回"Lazy value not initialized yet."
所以,只要先调用b.value,然后再进行打印输出,就能正常拿到对象b的值了。
那,上面那段代码的Java是怎么样的呢?
点击tools->kotlin->show Kotlin bytecode,然后点击右边弹窗上面的Decompile按钮,就出现了对应的Java代码。
private final Lazy a$delegate;
private Lazy b;
private final String getA() {
Lazy var1 = this.a$delegate;
Object var3 = null;
return (String)var1.getValue();
}
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(1300000);
Log.e("TAG", this.getA());
this.b.getValue();
Log.e("TAG", this.b.toString());
}
public MainActivity() {
this.a$delegate = LazyKt.lazy((Function0)null.INSTANCE);
this.b = LazyKt.lazy((Function0)null.INSTANCE);
}
这就简单明了多了。
注:如果在Kotlin Bytecode面板没有Decompile按钮,那可能是as没有装相关的插件plugin,那也容易
点击工具栏File->Settings,进入设置面板,在搜索栏输入plugins,进入插件界面,然后搜索Decompiler,安装并重启as就可以使用decompile功能了。