by lazy 还是 lateinit:在 Kotlin 中哪个更快?

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:Kotlin 开发者的永恒困境



如果您曾经使用 Kotlin 编写代码,并遇到需要延迟初始化属性的情况,那么您肯定问过自己:“到底该选择 by lazy 还是 lateinit?” 这个问题在 Stack Overflow 上非常热门(数千次浏览,数十次讨论)。这并非偶然:正确的选择不仅影响代码的可读性,还影响应用程序的 性能。



在本文中,我们不仅仅是复述文档。我们将进行真正的调查:剖析这两种机制的内部工作原理,比较它们的速度、内存消耗,并给出明确的建议。最后,我们将总结出具体的行动项。开始吧!



什么是 lateinit 和 by lazy?简要入门



首先,让我们回顾一下基本概念。这两种方法都解决同一个问题:将属性的初始化延迟到首次使用时。但它们的实现方式不同。



lateinit var —— 对编译器的承诺



关键字 lateinit 告诉编译器:“我保证在首次访问之前初始化这个属性,现在只需为它分配空间。” 它只能与 var 一起使用,并且仅用于 可变 属性。非常适合依赖注入(Dagger、Hilt、Koin)或在 Activity/Fragment 的 onCreate 中初始化的属性。



class MyActivity : AppCompatActivity() {    lateinit var repository: UserRepository

override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) repository = UserRepository() // 初始化 }

fun loadUser() { // 如果 repository 未初始化 — 将抛出 UninitializedPropertyAccessException repository.getUser() }}


by lazy —— 惰性委托



lazy 委托是一种更灵活的机制。它创建一个包装对象,该对象在首次访问时调用 lambda 并缓存结果。所有后续访问仅返回存储的值。默认情况下是线程安全的(同步的),但您可以配置模式。



class MyViewModel : ViewModel() {    val heavyData: ExpensiveData by lazy {        // 此代码块仅在首次访问时执行一次        ExpensiveData()    }}


这两种方法都解决了“惰性初始化”问题,但代价不同。让我们仔细分析这些代价。



性能:谁更快?



现在我们进入最有趣的部分——数据。 性能 不仅包括属性访问速度,还包括初始化开销、内存和线程安全性。



首次访问速度



lateinit 是绝对的冠军。在首次访问时,编译器仅检查该字段是否为 null(如果可为 null,则抛出异常)。没有 lambda,没有同步。这几乎是零成本的。



by lazy 较慢。在首次访问时,会调用一个同步块(如果使用默认模式 LazyThreadSafetyMode.SYNCHRONIZED),执行 lambda,并缓存结果。这需要微秒级的时间,但在高负载循环或关键代码中可能很明显。



重复访问速度



初始化后,两种方法的工作速度相同。 lateinit 只是读取字段(就像普通的 var)。 by lazy 在首次初始化后也读取缓存的值。在此阶段,速度差异趋近于零。



内存消耗



在这方面,lateinit 完胜。它不

博客

书籍推荐