Kotlin lazy 委托的底层实现原理

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

使用

kotlin 复制代码
val lazyVal: String by lazy {
    println("Computed")
    "Lazy"
}
println(lazyVal)  // 输出: Computed, Lazy
println(lazyVal)  // 输出: Lazy(不再计算)

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

底层实现原理

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

kotlin 复制代码
public fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)

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

Lazy<T> 接口:

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

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

lazy 默认使用 SynchronizedLazyImpl,其代码如下:

kotlin 复制代码
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 对象的调用。简化后的字节码(伪代码):

kotlin 复制代码
// 编译前
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 参数:

kotlin 复制代码
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 提供了一种高效、线程安全的延迟初始化机制。

相关推荐
念念不忘 必有回响2 小时前
viepress:vue组件展示和源码功能
前端·javascript·vue.js
C澒2 小时前
多场景多角色前端架构方案:基于页面协议化与模块标准化的通用能力沉淀
前端·架构·系统架构·前端框架
崔庆才丨静觅2 小时前
稳定好用的 ADSL 拨号代理,就这家了!
前端
江湖有缘2 小时前
Docker部署music-tag-web音乐标签编辑器
前端·docker·编辑器
恋猫de小郭3 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅10 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606111 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了11 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅11 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅11 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端