by lazy与= lazy

192 阅读2分钟

这篇文章,纯属较真。说它是八股文吧,不会问这么偏,说不是吧,但涉及到bylazy。而且,通常情况下,使用的都是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

2、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是怎么样的呢?

image.png

点击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,那也容易

image.png

点击工具栏File->Settings,进入设置面板,在搜索栏输入plugins,进入插件界面,然后搜索Decompiler,安装并重启as就可以使用decompile功能了。