美文网首页Android篇
Kotlin的延迟初始化

Kotlin的延迟初始化

作者: w达不溜w | 来源:发表于2021-03-21 14:00 被阅读0次

    Kotlin中有两种延迟初始化的方式。一种lateinit var,一种by lazy

    1.lateinit var
    class Test {
    
        private lateinit var str: String
    
        fun print() {
            str = "bestchangge"
            if (this::str.isInitialized) {
                println(str)
            }
        }
    
    }
    
    • 作用:告诉编译器在编译期不要去检查变量是否为空
    • 只能修饰变量var,不能修饰常量val
    • 不能修饰基本数据类型(因为基本数据类型在类加载的准备阶段会被初始化为默认值)
    • 在调用lateinit修饰的变量时,如果还没有初始化,则会抛出未初始化异常
    • 可以通过isInitialized检查是否初始化

    在AS中Tools—>Kotlin—>show Kotlin Bytecode—>Decompile反编译Kotlin字节码得到

    private String str;
    
    public final void print() {
      this.str = "bestchangge";
      if (((Test)this).str != null) {
        String var10000 = this.str;
        if (var10000 == null) {
          Intrinsics.throwUninitializedPropertyAccessException("str");
        }
    
        String var1 = var10000;
        boolean var2 = false;
        System.out.println(var1);
      }
    
    }
    

    可以看到:我们在引用变量的时候会进行判断,如果为空(也就是未初始化),就会抛出异常。

    2.by lazy
    class Test {
    
        private val str:String by lazy {
            println("开始初始化")
            //by lazy {}中最后一行代码,表示需要初始化的值
            "bestchangge"
        }
    
        fun print() {
            println(str)
        }
    }
    
    • 只能修饰常量val,不能修饰变量var
    • 在属性第一次使用时自动初始化,且只会加载一次(毕竟常量)

    by是属性委托关键字

    /**
     * Creates a new instance of the [Lazy] that uses the specified initialization function [initializer]
     * and the default thread-safety mode [LazyThreadSafetyMode.SYNCHRONIZED].
     */
    public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
    

    注释:使用指定的初始化函数[initializer]来创建一个[Lazy]类型的对象,默认的线程模型是[LazyThreadSafetyMode.SYNCHRONIZED]。默认返回了SynchronizedLazyImpl对象:

    //LazyJVM.kt
    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
            //重写value属性
        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
    
    }
    

    我们反编译Kotlin字节码得到

    public final class Test {
       private final Lazy str$delegate;
    
       private final String getStr() {
          Lazy var1 = this.str$delegate;
          Object var3 = null;
          boolean var4 = false;
          return (String)var1.getValue();
       }
    
       public final void print() {
          String var1 = this.getStr();
          boolean var2 = false;
          System.out.println(var1);
       }
    
       public Test() {
          this.str$delegate = LazyKt.lazy((Function0)null.INSTANCE);
       }
    }
    

    可以看到,生成了一个getter方法,return一个(String)var1.getValue(),也就是获取str会通过getValue。而SynchronizedLazyImpl实现了Lazy的value方法, 调用getValue方法返回_value, _value默认值是UNINITIALIZED_VALUE,表示为初始化,就会调用传入的initializer进行初始化,并把值赋给 _value,以后每次调用直接返回。

    3.小结

    lateinit var和by lazy都可以推迟初始化,lateinit var只是在编译期忽略对属性未初始化进行检查,何时初始化还需开发者自行决定。而by lazy在被第一次调用的时候能自动初始化,做到了真正的延迟初始化的行为。

    相关文章

      网友评论

        本文标题:Kotlin的延迟初始化

        本文链接:https://www.haomeiwen.com/subject/anincltx.html