ITmob-Ly
发布于 2023-01-04 / 111 阅读
0

Kotlin 中的 lateinit 和 lazy 有什么区别?

Kotlin-Blog

本文主要介绍下 Kotlin 上的 lateinitlazy 之间的区别。首先,我们介绍它们是什么,接下来,重点讨论我们应该在哪里使用哪一个。

什么是 lateinit?

通常,声明为非空类型的属性必须在构造函数中初始化。然而这样做通常并不方便。我们可能不想在声明时初始化我们的值,而是希望在以后随时初始化并使用它们。

要处理这种情况可以使用 lateinit 修饰符标记该属性,lateinit 关键字允许我们声明一个不可为 null 的属性,而无需立即初始化它。

示例:

lateinit var url: String

fun initializeUrl() {
    url = "https://itmob.cn"
}

fun getUrl() {
    if (::url.isInitialized) {
        println(url)
    } else {
        println("url not initialized")
    }
}

上面示例中,使用 lateinit 关键字声明一个属性 url。initializeUrl() 函数负责初始化 url 属性。使用 isInitialized 检查 url 是否已经初始化。如果它被初始化则输出它的值, 否则输出一条提升,表明它尚未初始化。

需要注意的是,lateinit 定义的属性在访问之前必须先初始化,如果我们在未初始化的情况下访问我们的值,将遇到 UninitializedPropertyAccessException 异常:

Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property url has not been initialized
	at cn.itmob.example.SampleKt.getUrl(SampleKt.kt:236)
	at cn.itmob.example.SampleKt.getUrl(SampleKt.kt:243)
	at cn.itmob.example.SampleKt.main(SampleKt.kt:252)
	at cn.itmob.example.SampleKt.main(SampleKt.kt)

Process finished with exit code 1

什么是 lazy?

lazy 是 Kotlin 中的函数,提供了一种方便的方法来创建延迟初始化。延迟初始化是我们经常用到的一种设计模式。延迟初始化意味着该属性仅在第一次访问时才会被初始化,对该属性的后续访问将返回缓存的值。

当我们想要创建一个对象但创建成本很高,对象的创建可能会导致依赖它的其他对象都会延迟创建,这种情况仅在要使用的地方初始化,而不是在应用程序启动时初始化很有用。

private val url: String by lazy {
    println("lazy initialization")
    "https://itmob.cn"
}

fun main() {
    println(url)
    println(url)
    println(url)
}

/* 输出:
lazy initialization
https://itmob.cn
https://itmob.cn
https://itmob.cn
*/

正如上例中看到的,访问 url 时它仅被初始化一次,之后每次再访问它时使用这个初始化的对象。

lateinit 和 lazy 有什么区别?

  1. 初始化的时间:这是它们最明显的区别,标记为 lateinit 的属性可以在运行时根据需要多次赋值,而标记为 lazy 的属性只能在第一次使用时赋值一次。
  2. 修饰符 lateinit 仅限于可变(var)变量属性,而函数 lazy 专门用于只读(val)属性。
  3. 线程安全:lazy 标记的属性,可以选择同步选项,例如:SYNCHRONIZEDPUBLICATIONNONE但不能确保 lateinit 属性的线程安全。(这就是为什么我们在使用单例设计模式时使用 lazy
  4. 原始类型的属性不允许使用 lateinit 修饰符,而 lazy 标记的属性可以是原始数据类型或非原始数据类型。(如:Int, Float
  5. 可不可空:lateinit 的属性不能为空,但 lazy 属性可以定义为 null
  6. 初始化之前访问 lateinit 属性会抛出未初始化属性的异常。 lazy 属性可以为 null,它仍会在第一次访问该属性时被初始化。