下面是一个用"Object"关键字装饰的A类:

object A {
}

反编译成Java代码后发现,它是通过静态代码块进行初始化的:

public final class A {
   public static final A INSTANCE;

   private A() {
   }

   static {
      A var0 = new A();
      INSTANCE = var0;
   }
}

我很好奇为什么Kotlin不使用静态内部类来实现Singleton,就像这样:

public final class A {
    public A getInstance() {
        return LazyHolder.INSTANCE;
    }

    private A() {
    }

    private static class LazyHolder {
        private static final A INSTANCE = new A();
    }
}

然后,在类A中使用变量"b"时,在A和getB()之间添加"getInstance()"方法:

public static final void main() {
    A.getInstance().getB();
}

这种单例模式支持延迟加载,可以避免内存浪费.为什么Kotlin不使用这种方法来实现"Object"关键字呢?

推荐答案

假设对象中没有@JvmStatic%/@JvmField%的值,那么当前的行为基本上与您提议的相同.

所有的初始化都在static initialiser中完成,这

在类为initialized时执行

T级是initialised分,

紧接在第一次出现以下任一情况之前:

  • T是一个类,并且创建了一个T的实例.

  • 调用由T声明的静态方法.

  • 分配一个由T声明的静态字段.

  • 使用由T声明的静态字段,并且该字段不是常量变量(§4.12.4).

因此,在您访问INSTANCE静态字段之前不会创建任何对象,Kotlin object的所有成员都驻留在该字段中.这基本上就是你的getInstance试图实现的目标,不是吗?你创建了自己的getInstance,有点像是在重新发明轮子.

有人甚至会争辩说,您的LazyHolder是一个需要加载的额外类,因此在这方面比当前的实现更糟糕.

当您引入@JvmStatic/@JvmField时,事情变得更加复杂,这两种方法都允许您在不经过INSTANCE的情况下访问Kotlin属性.

object O {
    @JvmStatic
    val static = 0
    val notStatic = 1
}

在本例中,如果您刚刚访问了O.static,则会创建一个对象,而您提出的解决方案确实解决了这个问题.

然而,您提出的解决方案也使对象中init个块的语义非常混乱--init个块中的代码何时执行?

请允许我举一个相当做作的例子:

object O {
    @JvmStatic
    val static: Int
    val notStatic: Int

    init {
        timeConsumingSideEffect()
        val x = iDontWantToRunThisTwice()
        static = timeConsumingComputation(x)
        notStatic = timeConsumingComputation(x)
    }
}

它们应该放在静态初始值设定项O还是LazyHolder中?

如果它们被放入O的静态初始值设定项中,那么它就不再是懒惰的了.一旦访问O.static,一切都会运行,就像当前的实现一样.

如果它们被放入LazyHolder的静态初始值设定项中,那么其他代码就有可能通过访问O.static来获得未初始化值.

虽然可以使用一些巧妙的数据流分析将负责初始化@JvmStatic属性的init块中的代码与初始化常规属性的代码分开,但timeConsumingSideEffect()何时应该运行仍不清楚.

语言设计很难!?

Kotlin相关问答推荐

Jetpack Compose中的数字 Select 器问题

在Jetpack Compose中创建波浪式文本动画:顺序中断问题

Kotlin .如何用从 1 到 90 的 5 个唯一数字填充列表中的每一行?

Kotlin:我可以将函数分配给 main 的伴随对象中的变量吗?

如何避免键盘打开时jetpack compose 内容上升

如何规避 Kotlin 的泛型类型差异约束

Kotlin 中的密封和内部有什么区别?

如何在 Kotlin 中为变量分配另一个变量的值?

嵌套数组 indexOf()

无法从 XML 访问 NavHostFragment

如何从 Java 中隐藏 Kotlin 的 lateinit var 支持字段?

Android Kotlin StringRes 数量String

如何使用Kotlin Dokka记录主构造函数参数

Kotlin - 来自 KType 的 KClass<*>

RecyclerView SnapHelper无法显示第一个/最后一个元素

Kotlin:使用Gradle进行增量编译

使用主构造函数时使用Kotlin getter/setter

Kotlin扩展函数与成员函数?

Kotlin中默认导入哪些包/函数?

如何在 Kotlin 中生成 MD5 哈希?