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的实现则没有任何同步措施,需要确保每次访问成员变量都是在同一个线程。