云计算、AI、云原生、大数据等一站式技术学习平台

网站首页 > 教程文章 正文

都2023年了,来聊聊Kotlin 的Lazy属性

jxf315 2025-03-23 20:41:59 教程文章 43 ℃


什么是Lazy属性?

在我们学习和工作中,会看到kotlin中出现以 by lazy这种语法糖修饰代码,第一次见面可能会觉得很新鲜:

这里先做一下总结,就是Kotlin的Lazy属性是一个很重要的概念,它可以帮助你实现延迟初始化,从而提高程序的运行性能。

如何使用Lazy属性

其实上图我们已经看到了 Lazy 的实现方式,那么这里我们可以重新开始一下,就以”String”字符串为例子,可以查看下面的代码:

我们在代码中新增了一句 println(‘computed’) 代码,以此来验证 Lazy 属性一个很重要的概念,这里我们先不说明,先执行如下代码再看看效果:

我们在main函数中打印了两次 lazyProperty 这个属性,发现两次打印的结果并不一致。我们可以看到,只有第一次打印时,才会出现 “computed!” 字样,第二次则不会出现。那么这里就引申出 Lazy 属性的特有的运行逻辑:

lazyProperty 的值只会在第一次访问时计算。之后每次访问都会直接返回之前计算好的值。

lazy 属性的内部属性

我们使用了 lazy 这个属性,为何不看看其内部的实现方式呢?程序员不都是喜欢刨根问底的么?

其内部实现方式也挺简单的,简单概括一下如下:

Lazy 属性实际上是通过委托实现的。具体来说,它使用了 Lazy 委托,其中 T 是lazy属性返回值的类型。

Lazy 委托维护了一个 value 属性,该属性就是第一次初始化返回的值。同时它也有一个 isInitialized 标志位,用于记录value是否已经被初始化。

当我们第一次访问 lazy 属性时,如果 isInitialized 为false,则会计算value的值,并将 isInitialized 设置为 true。之后每次访问就直接返回 value 值。所以简单来说,lazy的内部实现如下:

这就是 Kotlin.lazy 属性的内部原理。它利用委托和延迟初始化,来实现只初始化一次的效果。

Lazy 的应用实例

当然,在你使用 kotlin 得心应手时,你会发现 Lazy 属性有很多用处,以下就来几个应用中的实例。

  1. 可以避免非必要的初始化:当属性的值初始化比较耗时时,但并非一定要使用得到,那么定义 lazy 可以避免非不要的初始化,从而提供性能;
  2. 从上面的源码中可以看到,Lazy 的源码中是带了 lock 的。那么就意味着 Lazy 属性是线程安全的,可以适用于多线程的环境;
  3. 可以解决循环依赖的问题:当两个对象相互依赖时,不使用Lazy的话将会产生循环依赖,但是 Lazy 属性可以解决这个问题。
  1. 延迟加载,实现按需加载:可以只在首次访问属性时进行初始化,实现按需加载的效果。
  2. 在一些测试场景中使用:你可以使用 lazy 属性定义一个耗时初始化依赖,但是大部分测试中不会访问到这个属性,这样就不会产生性能损耗,但当需要这个属性的测试时,有可以正常工作。

by lazy 和 lateinit 之间有什么不同

我们在使用by lazy 的同时,肯定也会回到使用 lateinit 修饰的变量,lateinit通过字面的意思,也是延迟初始化的意思,那么它们之间有什么不同呢?

大致概括起来,通过以上代码的分析,可以得出的结论有如下几点:

  1. Lateinit 只适用于 var,但是by lazy只适用于 val;
  2. Lateinit 使用支持字段来存储结果的值,但是by lazy 使用委托对象;
  3. By lazy 初始化默认是线程安全的,lateinit var 修饰符跨多个线程的初始化取决于用户的代码。

那么,说了这么多,到底什么条件下使用 by lazy?什么情况下使用 lateinit 呢?那么就举一下例子:

在使用 by lazy时:

  1. 不一定非要初始化,只是我们想的时候再去初始化;
  2. 想初始化一个变量,并在整个代码中使用它;
  3. 只需要变量的可读属性时。

在使用lateinit时:

  1. 需要延迟初始化一个变量;
  2. 想在使用它之前初始化一个变量;
  3. 正在使用var 或者可变属性;
  4. 变量可能会在稍后阶段发生变化。

好了,今天的分享就到这里了吧。

最近发表
标签列表