Kotlin中有两种延迟初始化的方式。一种lateinit var,一种by lazy
1.lateinit var
class Test {
private lateinit var str: String
fun print() {
str = "bestchangge"
if (this::str.isInitialized) {
println(str)
}
}
}
- 作用:告诉编译器在编译期不要去检查变量是否为空
- 只能修饰变量var,不能修饰常量val
- 不能修饰基本数据类型(因为基本数据类型在类加载的准备阶段会被初始化为默认值)
- 在调用lateinit修饰的变量时,如果还没有初始化,则会抛出未初始化异常
- 可以通过isInitialized检查是否初始化
在AS中Tools—>Kotlin—>show Kotlin Bytecode—>Decompile反编译Kotlin字节码得到
private String str;
public final void print() {
this.str = "bestchangge";
if (((Test)this).str != null) {
String var10000 = this.str;
if (var10000 == null) {
Intrinsics.throwUninitializedPropertyAccessException("str");
}
String var1 = var10000;
boolean var2 = false;
System.out.println(var1);
}
}
可以看到:我们在引用变量的时候会进行判断,如果为空(也就是未初始化),就会抛出异常。
2.by lazy
class Test {
private val str:String by lazy {
println("开始初始化")
//by lazy {}中最后一行代码,表示需要初始化的值
"bestchangge"
}
fun print() {
println(str)
}
}
- 只能修饰常量val,不能修饰变量var
- 在属性第一次使用时自动初始化,且只会加载一次(毕竟常量)
by是属性委托关键字
/**
* Creates a new instance of the [Lazy] that uses the specified initialization function [initializer]
* and the default thread-safety mode [LazyThreadSafetyMode.SYNCHRONIZED].
*/
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
注释:使用指定的初始化函数[initializer]来创建一个[Lazy]类型的对象,默认的线程模型是[LazyThreadSafetyMode.SYNCHRONIZED]。默认返回了SynchronizedLazyImpl对象:
//LazyJVM.kt
private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
private var initializer: (() -> T)? = initializer
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
// final field is required to enable safe publication of constructed instance
private val lock = lock ?: this
//重写value属性
override val value: T
get() {
val _v1 = _value
if (_v1 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST")
//①已经初始化,直接返回
return _v1 as T
}
//使用同步方法,避免多次初始化
return synchronized(lock) {
val _v2 = _value
if (_v2 !== UNINITIALIZED_VALUE) {
//再次检查是否初始化
@Suppress("UNCHECKED_CAST") (_v2 as T)
} else {
//②如果未初始化,调用传入的值进行初始化
val typedValue = initializer!!()
_value = typedValue
initializer = null
typedValue
}
}
}
//是否已经初始化
override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
}
我们反编译Kotlin字节码得到
public final class Test {
private final Lazy str$delegate;
private final String getStr() {
Lazy var1 = this.str$delegate;
Object var3 = null;
boolean var4 = false;
return (String)var1.getValue();
}
public final void print() {
String var1 = this.getStr();
boolean var2 = false;
System.out.println(var1);
}
public Test() {
this.str$delegate = LazyKt.lazy((Function0)null.INSTANCE);
}
}
可以看到,生成了一个getter方法,return一个(String)var1.getValue(),也就是获取str会通过getValue。而SynchronizedLazyImpl实现了Lazy的value方法, 调用getValue方法返回_value, _value默认值是UNINITIALIZED_VALUE,表示为初始化,就会调用传入的initializer进行初始化,并把值赋给 _value,以后每次调用直接返回。
3.小结
lateinit var和by lazy都可以推迟初始化,lateinit var只是在编译期忽略对属性未初始化进行检查,何时初始化还需开发者自行决定。而by lazy在被第一次调用的时候能自动初始化,做到了真正的延迟初始化的行为。
网友评论