英文:
Can a parent class create an instance of its subclass?
问题
我在一些Java代码上使用Sonarqube运行了单元测试,发现其中一个问题如下:
> 类不应在初始化期间访问它们自己的子类
> 当父类在自身初始化期间引用子类的成员时,结果可能不如预期,因为子类可能尚未初始化。这可能会创建所谓的“初始化循环”,甚至在某些极端情况下可能会导致死锁。
这是Sonarqube用于描述此问题的示例代码:
class Parent {
static int field1 = Child.method(); // 非法
static int field2 = 42;
public static void main(String[] args) {
System.out.println(Parent.field1); // 将显示“0”而不是“42”
}
}
class Child extends Parent {
static int method() {
return Parent.field2;
}
}
接下来是引发问题的代码的简化版本。
abstract class Parent {
static Parent childInstance = new Child();
}
我不太明白为什么会有这个问题。在Sonarqube的示例中,父类通过调用子类的静态方法来初始化field1
。这意味着在调用方法之前我不需要实例化一个子类。
在第二段代码片段中,父类试图实例化子类,而不是调用其方法。
您能否解释一下为什么不可能在父类中引用子类?
编辑:我正在使用用于Maven的SonarScanner,我的Sonarqube版本是8.4.1.35646。该问题的规则ID是S2390。
英文:
I ran unit tests with Sonarqube on some Java code and one of the issues I found is the following.
> Classes should not access their own subclasses during initialization
> When a parent class references a member of a subclass during its own initialization, the results might not be what you expect because the child class might not have been initialized yet. This could create what is known as an "initialisation cycle", or even a deadlock in some extreme cases.
This is the example code that Sonarqube used to describe the issue:
class Parent {
static int field1 = Child.method(); // Noncompliant
static int field2 = 42;
public static void main(String[] args) {
System.out.println(Parent.field1); // will display "0" instead of "42"
}
}
class Child extends Parent {
static int method() {
return Parent.field2;
}
}
Next is a simplification of the code that raised the issue.
abstract class Parent {
static Parent childInstance = new Child();
}
I don't really understand why this is an issue. In the Sonarqube example, the parent is initializing field1
by calling the static method of the child. This means that I don't need to instantiate a child before calling the method.
In the second snippet of code, the parent is trying to instantiate the child rather than calling one of its methods.
Can you please explain to me why it's not possible to refer to the child in the parent class?
EDIT: I'm using SonarScanner for Maven and my Sonarqube version is 8.4.1.35646. The rule id of the issue is S2390.
答案1
得分: 1
考虑代码在这里执行的情况:
抽象类 Parent {
static Parent childInstance = new Child();
}
由于你正在给一个 static
字段赋值,new Child()
必须在第一个 Parent
实例创建之前很久就执行了。
也就是在类初始化期间(为了简单起见,让我们假设这等同于 "类加载",尽管这并不完全准确)。
这意味着在初始化类 Parent
时,它会创建一个新的 Child
实例。通常,为了完成初始化,首先需要完成的一件事是超类已经初始化。由于我们正在初始化 Parent
,所以我们可以知道 Child
不能已经完全初始化(根据定义,因为这需要 Parent
完全初始化)。
JVM/Java 语言规范实际上允许这种结构起作用,但它们有一些缺点(比如能够观察到未初始化的 final
字段)。
可以这样理解:就好像你正在忙于建造地下室时,还想从房子的二楼拿些东西。
英文:
Think about when code is executed here:
abstract class Parent {
static Parent childInstance = new Child();
}
Since your assigning to a static
field the new Child()
has to be executed long before the first Parent
instance is created.
Namely during class initialization (for simplicities sake, let's assume that's equivalent to "class loading", even though that's not quite accurate).
That means that while the class Parent
is being initialized it creates a new instance of Child
. Usually one of the first thing that needs to happen for initialization to complete is that the super class is already initialized. Since we're currently initializing Parent
we can know that Child
can't already be fully initialized (by definition, since that would require Parent
to be fully initialized).
There are some "tricks" that the JVM/Java language spec plays to actually allow this construct to work, but they have drawbacks (such as being able to observe uninitialized final
fields).
Think of it that way: you are trying to grab something from the second story of a house while you're busy building the basement.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论