美文网首页
kotlin-by lazy实现原理

kotlin-by lazy实现原理

作者: zzq_nene | 来源:发表于2020-07-28 16:49 被阅读0次

一、lazy的定义

lazy是一个定义在LazyJVM中的函数,有两种实现。这里看其中一种:

/**
 * Creates a new instance of the [Lazy] that uses the specified initialization function [initializer]
 * and the default thread-safety mode [LazyThreadSafetyMode.SYNCHRONIZED].
 *
 * If the initialization of a value throws an exception, it will attempt to reinitialize the value at next access.
 *
 * Note that the returned instance uses itself to synchronize on. Do not synchronize from external code on
 * the returned instance as it may cause accidental deadlock. Also this behavior can be changed in the future.
 */
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)

lazy其实就是使用默认的初始化函数initializer来创建一个lazy类型的对象,并且使用的默认的线程模型,是LazyThreadSafetyMode.SYNCHRONIZED

二、实例分析

class LazyLoadinigTest{

    private val name: String by lazy{"1111"}

    fun printName() {
        println(name)
    }
}

fun main(args: Array<String>) {
    val test = LazyLoadinigTest()
    test.printName()
}

将这个代码转成kotlin字节码进行分析,使用tools->kotlin->show kotlin bytecode
第一部分就是构造函数的字节码:

  public <init>()V
   L0
    LINENUMBER 8 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
   L1
    LINENUMBER 10 L1
    ALOAD 0
    GETSTATIC com/nene/kttest/LazyLoadinigTest$name$2.INSTANCE : Lcom/nene/kttest/LazyLoadinigTest$name$2;
    CHECKCAST kotlin/jvm/functions/Function0
    INVOKESTATIC kotlin/LazyKt.lazy (Lkotlin/jvm/functions/Function0;)Lkotlin/Lazy;
    PUTFIELD com/nene/kttest/LazyLoadinigTest.name$delegate : Lkotlin/Lazy;
    RETURN
   L2
    LOCALVARIABLE this Lcom/nene/kttest/LazyLoadinigTest; L0 L2 0
    MAXSTACK = 2
    MAXLOCALS = 1

从这里可以看出,编译成字节码之后,构造器中并没有定义一个name属性,而是定义了一个namedelegate属性,定义这个属性的时候,首先获取一个静态变量name2.INSTANCE,然后检查是否可以转成Function0类型,并且将这个静态变量作为属性值传入LazyKt.lazy函数中,获取到返回值,接着将这个返回值赋值给了namedelegate 所以namedelegate=LazyKt.lazy((Function0)name2.INSTANCE) name2是一个编译生成的内部类
从这里可以看出,name$2.INSTANCE其实就是()->T的一种具体实现
而延迟加载,其实就是在使用的时候才加载数据,所以name属性需要看其getter函数,即getName()。

  private final getName()Ljava/lang/String;
   L0
    ALOAD 0
    GETFIELD com/nene/kttest/LazyLoadinigTest.name$delegate : Lkotlin/Lazy;
    ASTORE 1
    ALOAD 0
    ASTORE 2
    GETSTATIC com/nene/kttest/LazyLoadinigTest.$$delegatedProperties : [Lkotlin/reflect/KProperty;
    ICONST_0
    AALOAD
    ASTORE 3
   L1
    ALOAD 1
    INVOKEINTERFACE kotlin/Lazy.getValue ()Ljava/lang/Object;
   L2
    CHECKCAST java/lang/String
    ARETURN
   L3
    LOCALVARIABLE this Lcom/nene/kttest/LazyLoadinigTest; L0 L3 0
    MAXSTACK = 2
    MAXLOCALS = 4

这段字节码转成java伪代码,其实可以变成:

private final int getName(){
    Lazy var1 = this.name$delegate;
    KProperty var2 = this.$$delegatedProperties[0]
    return ((Number)var1.getValue()).intValue()
}

从这里可以看出,name的getter函数最终是返回了namedelegate的getValue()函数返回的值 而namedelegate,又是由LazyKt.lazy()获取到的返回值,所以看LazyKt.lazy函数,而这个函数其实就是LazyJVM.lazy()
所以其函数返回值就是SynchronizedLazyImpl对象

三、SynchronizedLazyImpl

// 继承Lazy并且实现了Serializable接口
// 数据返回其实就是返回的SynchronizedLazyImpl.getValue()
private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
    // 定义一个() -> T高阶函数类型的变量并且赋初始值initializer
    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 {
                    // 调用传入的lambda表达式进行初始化
                    // 并且保存结果,然后返回
                    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)
}

SynchronizedLazyImpl源码中,是继承自Lazy;并且其内部定义了一个volatile修饰的_value属性,这样做的目的是当一个线程修改属性值的时候,其他线程可以得到最新的值。
而SynchronizedLazyImpl的getValue()函数,其实就是重写其value的get(),在这里就是做判断,判断_value是否已经初始化,不管是被当前线程还是其他线程,如果已经初始化,则直接返回,如果没有被初始化,则调用传入的lambda表达式进行初始化,得到lambda表达式的返回值进行赋值,然后再将lambda表达式类型对象置为null

相关文章

  • kotlin-by lazy实现原理

    一、lazy的定义 lazy是一个定义在LazyJVM中的函数,有两种实现。这里看其中一种: lazy其实就是使用...

  • Kotlin lazy 实现原理

    上面的函数体为 lazy 的实现代码,该函数的参数为接收一个无参并返回值为 T 类型的函数,然后它的实现是 Syn...

  • Kotlin lateinit 和 by lazy实现原理

    lateinit lateinit:用来修饰var类型成员变量,用来表示该变量可以在晚些时候初始化,用来避免不必要...

  • Kotlin 单例

    单例的实现方法,可以通过同伴对象,或者 lazy。示例: 通过 lazy 实现 参考《Programming Ko...

  • Kotlin的by 委托

    Kotlin的by 委托 1. by lazy的原理解析 我们用kotlin经常会用到by lazy,所以我之前一...

  • vue-lazyload探索

    原理简述 vue-lazyload是通过指令的方式实现的,定义的指令是v-lazy指令指令被bind时会创建一个l...

  • React拆分组件的最佳实践?

    目前常用的做法使用 React 的 lazy 和 suspence 在组件维度实现拆分资源包。 lazy + Su...

  • kotlin—lazy及其原理

    1、lazy简介 lazy是属性委托的一种,是有kotlin标准库实现。它是属性懒加载的一种实现方式,在对属性使用...

  • 08-属性

    属性 存储属性 计算属性 注:不能只有set 枚举rawValue原理 延迟存储属性(Lazy Stored Pr...

  • 五十三、Elasticsearch聚合分析--fielddata

    1、fielddata核心原理 fielddata加载到内存的过程是lazy加载的,对一个analyzed fie...

网友评论

      本文标题:kotlin-by lazy实现原理

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