lateinit 关键字用来修饰一个类的非空成员变量,表示该成员变量的值在稍后合适的时机会初始化,例如:
class Test {
lateinit var name: String
fun test() {
if (::name.isInitialized) {
println("name is initialized")
println(name)
在给 lateinit 修饰的成员变量赋值之前如果有代码试图访问该成员变量的值,则会直接抛出异常。在访问 lateinit 修饰的成员变量之前可以先用 isInitialized 来判断该成员变量是否已经赋值了。
上述代码转成字节码之后再反编译后如下:
public final class Test {
public String name;
@NotNull
public final String getName() {
String var10000 = this.name;
if (var10000 == null) {
Intrinsics.throwUninitializedPropertyAccessException("name");
return var10000;
public final void setName(@NotNull String var1) {
Intrinsics.checkNotNullParameter(var1, "<set-?>");
this.name = var1;
public final void test() {
String var1;
if (((Test)this).name != null) {
var1 = "name is initialized";
System.out.println(var1);
String var10000 = this.name;
if (var10000 == null) {
Intrinsics.throwUninitializedPropertyAccessException("name");
var1 = var10000;
System.out.println(var1);
从上述代码可以看出,编译器会为 lateinit 修饰的成员变量name生成get方法和set方法,在调用get方法的时候会判断变量是否已经初始化,若未初始化则会抛出 UninitializedPropertyAccessException
isInitialized 是kotlin.reflect.KProperty0的一个扩展属性,可以用于判断某个成员变量是否已经初始化。
lazy 用于延迟初始化一个成员变量到其首次被访问的时候,且该成员变量只会被初始化一次,例如:
class Test {
val name: String by lazy {
"william"
fun test() {
println(name)
lazy 需要配合by关键字来使用,表示将 name 的初始化由lazy代理完成。因为只会被初始化一次,所以 lazy 只能用于 val 修饰的成员变量。
lazy 是一个高阶函数,其具体实现在LazyJVM.kt中:
public interface Lazy<out T> {
* Gets the lazily initialized value of the current Lazy instance.
* Once the value was initialized it must not change during the rest of lifetime of this Lazy instance.
public val value: T
* Returns `true` if a value for this Lazy instance has been already initialized, and `false` otherwise.
* Once this function has returned `true` it stays `true` for the rest of lifetime of this Lazy instance.
public fun isInitialized(): Boolean
public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
其接受一个初始化函数作为参数,返回一个SynchronizedLazyImpl对象,该对象实现了Lazy接口。SynchronizedLazyImpl 从其名字上就可以看出它使用synchronized关键字确保了其初始化变量的过程是线程安全的。
如果我们能够确认成员变量的初始化一定是线程安全的,那么可以使用另一个lazy方法来指定LazyThreadSafeMode,如下所示:
public actual fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
when (mode) {
LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
class Test {
val name: String by lazy (LazyThreadSafeMode.NONE) {
"william"
fun test() {
println(name)
查看SafePublicationLazyImpl的实现可以发现其为成员变量加了Volatile关键字修饰,可以保证可见性。而UnsafeLazyImpl的实现则没有任何同步措施,需要确保每次访问成员变量都是在同一个线程。