掘金 人工智能 07月06日 15:18
Kotlin lazy 委托的底层实现原理
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文深入探讨了 Kotlin 中的 lazy 属性委托,这是一种实现延迟初始化的强大机制。通过将属性的 getter 操作委托给其他对象,lazy 委托允许仅在首次访问属性时才进行初始化,从而优化性能,尤其是在处理开销较大的对象时。文章详细介绍了 lazy 的使用方法、底层实现原理,包括 SynchronizedLazyImpl 的双重检查锁机制,以及不同线程安全模式的选择。文章末尾总结了 lazy 的优势和适用场景,强调了其在提高代码效率和线程安全方面的作用,为开发者提供了实用的参考。

💡 **基本用法与特性**: lazy 委托用于实现属性的延迟初始化,仅在首次访问时执行初始化,后续访问则直接返回已缓存的值,有效避免不必要的资源开销,提高程序性能。

⚙️ **底层实现原理**: lazy 是一个高阶函数,返回 Lazy 接口的实现 SynchronizedLazyImpl。SynchronizedLazyImpl 内部使用 @Volatile 保证多线程可见性,通过双重检查锁(Double-Checked Locking)确保线程安全,仅初始化一次。

🔑 **核心机制**: lazy 委托将属性的 get 操作委托给 Lazy 对象,首次访问时,Lazy 对象调用 initializer 初始化属性值,并将结果缓存。后续访问直接返回缓存值,无需再次初始化。

🛡️ **线程安全模式**: lazy 支持不同的线程安全模式,通过 LazyThreadSafetyMode 参数进行配置。默认的 SYNCHRONIZED 模式适用于多线程环境,保证线程安全。其他模式如 NONE 和 PUBLICATION 适用于特定场景,开发者可以根据需要选择。

lazy 委托是 Kotlin 的一种属性委托,用于实现延迟初始化。所谓属性委托,就是将属性的 getter 和 setter 操作委托给其他对象来处理。lazy 委托允许我们在第一次访问属性时才进行初始化,后续访问直接返回已缓存的值。这种机制可以提高性能,避免不必要的开销,尤其是在处理开销较大的对象时。

使用

val lazyVal: String by lazy {    println("Computed")    "Lazy"}println(lazyVal)  // 输出: Computed, Lazyprintln(lazyVal)  // 输出: Lazy(不再计算)

lazyVal 使用 lazy 委托延迟初始化,只有首次访问时执行初始化块 { println("Computed"); "Lazy" },后续访问直接返回缓存值。

底层实现原理

lazy 是一个高阶函数,定义在 Kotlin 标准库中:

public fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)

参数 initializer: () -> T 是一个无参 Lambda,返回类型为 T(此处 TString)。返回 Lazy<T> 接口实例,具体实现是 SynchronizedLazyImpl

Lazy<T> 接口:

public interface Lazy<out T> {    val value: T // 获取委托值    fun isInitialized(): Boolean // 检查是否已初始化}

这里把 lazyVal 的初始化逻辑封装在一个 Lazy 对象中,后续访问 lazyVal 时,实际上是访问这个 Lazy 对象的 value 属性。

lazy 默认使用 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 to ensure safe publication of 'SynchronizedLazyImpl' itself through  // var lazy = lazy() {}  private val lock = lock ?: this // 使用自身作为锁  override val value: T      get() {          val _v1 = _value          // 如果 _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!!() // 调用 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)}

_value 初始为 UNINITIALIZED_VALUE(哨兵对象)。首次访问 value 时,检查 _value 是否未初始化,若是则调用 initializer()(即 { println("Computed"); "Lazy" })。

使用 @Volatile 确保 _value 的可见性。synchronized(lock) 实现双重检查锁(Double-Checked Locking),保证多线程环境下初始化只执行一次。

初始化后,_value 保存结果("Lazy"),后续访问直接返回,无需再次调用 initializer

Kotlin 编译器将 lazyVal 的访问转换为对 Lazy 对象的调用。简化后的字节码(伪代码):

// 编译前val lazyVal: String by lazy { println("Computed"); "Lazy" }// 编译后(大致等效)private val lazyVal$delegate: Lazy<String> = lazy { println("Computed"); "Lazy" }val lazyVal: String    get() = lazyVal$delegate.value

第一次 lazyVal 访问调用 lazyVal$delegate.valueSynchronizedLazyImpl 执行 initializer,打印 Computed,返回 "Lazy",并缓存。

第二次访问直接返回缓存的 _value"Lazy"),无 initializer 调用。

lazy 支持不同线程安全模式,通过 LazyThreadSafetyMode 参数:

val lazyVal: String by lazy(LazyThreadSafetyMode.NONE) { "Lazy" } // 无同步,单线程使用val lazyValPub: String by lazy(LazyThreadSafetyMode.PUBLICATION) { "Lazy" } // 允许多线程初始化,最终一致

默认 SYNCHRONIZED(如上述代码)适合多线程场景。

总结

属性委托(by)将 get 操作转发给 Lazylazy 委托通过 SynchronizedLazyImpl 实现延迟初始化。使用双重检查锁确保线程安全,首次访问执行 initializer,后续返回缓存值。编译器将 lazyVal 转换为 Lazy 对象的 value 访问。

lazy 适合昂贵初始化的场景(如数据库连接、配置加载)。注意线程安全模式的选择(默认 SYNCHRONIZED 适合多数场景)。

通过 lazy 委托,Kotlin 提供了一种高效、线程安全的延迟初始化机制。

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

Kotlin 属性委托 lazy 延迟初始化
相关文章