首页 > 解决方案 > 父类可以创建其子类的实例吗?

问题描述

我使用 Sonarqube 对一些 Java 代码进行了单元测试,我发现的问题之一如下。

类在初始化期间不应访问自己的子类

当父类在其自己的初始化期间引用子类的成员时,结果可能不是您所期望的,因为子类可能尚未初始化。这可能会产生所谓的“初始化周期”,甚至在某些极端情况下会出现死锁。

这是 Sonarqube 用来描述问题的示例代码:

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;
  }
}

接下来是引发问题的代码的简化。

abstract class Parent {
    static Parent childInstance = new Child();
}

我真的不明白为什么这是一个问题。在 Sonarqube 示例中,父级field1通过调用子级的静态方法进行初始化。这意味着我不需要在调用该方法之前实例化一个孩子。

在第二段代码中,父级试图实例化子级,而不是调用其中一个方法。

你能解释一下为什么不能在父类中引用孩子吗?

编辑:我正在使用 SonarScanner for Maven,我的 Sonarqube 版本是 8.4.1.35646。问题的规则 ID 是 S2390。

标签: javainheritancesonarqube

解决方案


想想这里的代码是什么时候执行的:

abstract class Parent {
    static Parent childInstance = new Child();
}

由于您分配给一个static字段,new Child()因此必须在Parent创建第一个实例之前很久执行。

即在类初始化期间(为了简单起见,我们假设这相当于“类加载”,即使这不太准确)。

这意味着当类Parent被初始化时,它会创建一个新的Child. 通常,完成初始化需要发生的第一件事是超类已经初始化。由于我们当前正在初始化Parent,我们可以知道Child它还不能完全初始化(根据定义,因为这需要Parent完全初始化)。

JVM/Java 语言规范使用了一些“技巧”来实际允许此构造工作,但它们有缺点(例如能够观察未初始化的final字段)。

可以这样想:当你忙着建造地下室时,你正试图从房子的二楼拿东西。


推荐阅读