Kotlin by lazy和lateinit的使用及区别
在 Kotlin 中,lateinit
和 by lazy
都可以用来延迟初始化变量,但它们有不同的适用场景和使用方式。下面详细介绍它们的用法和区别。
1.lateinit
lateinit
关键字用于延迟初始化 可变属性 (var
),主要用于那些在声明时不能立即初始化,但保证在使用前会完成初始化的属性。lateinit
只能用于非空的 可变变量,且只能用于 类的属性(不能用于局部变量或常量)。
使用场景
lateinit
通常用于 Android 的 Context
、View
、Presenter
等需要在声明时无法立即赋值,但稍后会赋值的情况。
示例代码
class Example {
lateinit var text: String // 延迟初始化的变量
fun initializeText() {
text = "Hello, World!"
}
fun printText() {
if (this::text.isInitialized) { // 检查是否已经初始化
println(text)
} else {
println("text 未初始化")
}
}
}
fun main() {
val example = Example()
example.initializeText()
example.printText() // 输出:Hello, World!
}
注意事项
- 只适用于可变变量:
lateinit
只能用于var
变量,不能用于val
。 - 非空类型:
lateinit
只能用于非空类型,不能用于原始数据类型(如Int
、Double
等)。 - 是否初始化检查:可以使用
this::property.isInitialized
检查属性是否已初始化,避免直接访问未初始化的变量引发异常。
2. by lazy
by lazy
适用于 不可变属性 (val
),它的初始化只会在第一次被访问时触发,且结果会被缓存下来,以后每次访问时都返回相同的值。by lazy
可以用来延迟初始化那些计算量大或依赖其他数据的属性,通常适用于线程安全的场景。by lazy
本身是一种属性委托。属性委托的关键字是by。
使用场景
适合用来初始化 只读变量,比如配置文件加载、耗时计算结果等。
示例代码
class Example {
val text: String by lazy {
println("初始化 text")
"Hello, Lazy!"
}
fun printText() {
println(text) // 第一次访问时初始化
}
}
fun main() {
val example = Example()
example.printText() // 输出:初始化 text\nHello, Lazy!
example.printText() // 输出:Hello, Lazy!(不会再初始化)
}
注意事项
- 只适用于只读变量:
by lazy
只能用于val
变量,表示它是不可变的。 - 线程安全:
by lazy
的默认模式是线程安全的(相当于LazyThreadSafetyMode.SYNCHRONIZED
),也可以选择其他模式,如LazyThreadSafetyMode.PUBLICATION
或LazyThreadSafetyMode.NONE
。 - 延迟计算:只有在属性第一次被访问时才会初始化。
3. lateinit
和 by lazy
的区别
特性 | lateinit | by lazy |
---|---|---|
适用类型 | var 可变变量 | val 不可变变量 |
适用数据类型 | 只能用于非空对象类型 | 适用于任何类型,包括基础数据类型 |
初始化时机 | 在使用前需要手动赋值 | 第一次访问时自动初始化 |
初始化次数 | 可以被多次赋值 | 只会被初始化一次 |
线程安全性 | 无内置的线程安全 | 默认是线程安全的(可更改模式) |
使用场景 | Android 中的 Context 、View 等 | 计算量大的只读变量,懒加载 |
是否可检查初始化状态 | 可以使用 isInitialized 检查 | 无法检查是否已初始化 |
总结
- 使用
lateinit
:适用于那些需要稍后赋值且会被多次修改的对象属性,比如 Android 中的Context
或View
。 - 使用
by lazy
:适用于那些不需要立即初始化,且一旦初始化后不再改变的只读属性,比如计算密集型或一次性加载的属性。 - 一般来说的话,lateinit一定要记得初始化,不然后面会爆异常UninitializedPropertyAccessException