下面是一个用“object”关键字修饰的类A:
object A {
}
在将其反编译成Java代码后,发现它是通过静态代码块初始化的:
public final class A {
public static final A INSTANCE;
private A() {
}
static {
A var0 = new A();
INSTANCE = var0;
}
}
我很好奇为什么Kotlin不使用静态内部类来实现单例,就像这样:
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”关键字?
1条答案
按热度按时间v64noz0r1#
当前的行为基本上与您的建议相同,假设您在对象中没有任何
@JvmStatic
/@JvmField
。所有的初始化都是在静态初始化器中完成的,它
当类被 * 初始化 * 时执行
并且类
T
被初始化,在以下任何一种情况首次发生之前:
T
是一个类,并创建了T
的示例。T
声明的静态方法。T
声明的静态字段被赋值。T
声明的静态字段,并且该字段不是常量变量(§4.12.4)。因此,在访问
INSTANCE
静态字段之前,不会创建任何对象,Kotlinobject
的所有成员都驻留在该字段中。这基本上就是getInstance
试图实现的目标,不是吗?通过创建自己的getInstance
,您可以重新发明轮子。有人甚至会说
LazyHolder
是一个需要加载的额外类,因此在这方面比当前的实现更糟糕。当你引入
@JvmStatic
/@JvmField
时,事情变得更加复杂,这两个都允许你访问Kotlin属性,而不需要通过INSTANCE
。在本例中,如果您刚刚访问了
O.static
,则会创建一个对象,而您提出的解决方案确实解决了这个问题。但是,您提出的解决方案也使对象中
init
块的语义非常混乱-什么时候执行init
块中的代码?请允许我举一个相当做作的例子:
它们应该放在
O
或LazyHolder
的静态初始化器中吗?如果它们被放在
O
的静态初始化器中,那么它就不再是懒惰的了。只要你访问O.static
,一切都会运行,就像当前的实现一样。如果将它们放在
LazyHolder
的静态初始化器中,则其他代码可能会通过访问O.static
获得未初始化的值。虽然可以使用一些巧妙的数据流分析将
init
块中负责初始化@JvmStatic
属性的代码与那些初始化常规属性的代码分开,但仍然不清楚timeConsumingSideEffect()
何时应该运行。语言设计很难!😅