本文主要介绍下 Kotlin 上的 lateinit
和 lazy
之间的区别。首先,我们介绍它们是什么,接下来,重点讨论我们应该在哪里使用哪一个。
什么是 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 有什么区别?
- 初始化的时间:这是它们最明显的区别,标记为
lateinit
的属性可以在运行时根据需要多次赋值,而标记为lazy
的属性只能在第一次使用时赋值一次。 - 修饰符
lateinit
仅限于可变(var)变量属性,而函数lazy
专门用于只读(val)属性。 - 线程安全:
lazy
标记的属性,可以选择同步选项,例如:SYNCHRONIZED
、PUBLICATION
或NONE
,但不能确保lateinit
属性的线程安全。(这就是为什么我们在使用单例设计模式时使用lazy
) - 原始类型的属性不允许使用
lateinit
修饰符,而lazy
标记的属性可以是原始数据类型或非原始数据类型。(如:Int
,Float
) - 可不可空:
lateinit
的属性不能为空,但 lazy 属性可以定义为 null - 初始化之前访问
lateinit
属性会抛出未初始化属性的异常。lazy
属性可以为null
,它仍会在第一次访问该属性时被初始化。