Enum的枚举为NULL

Mar*_*lho 11 java null enums reference

我正在为Java 1.6上的大学课程开发一个LALG编译器.所以我做了一个类类和语法类.

EnumTypes

public enum EnumTypes {

    A("OLA"),
    B("MUNDO"),
    C("HELLO"),
    D("WORLD"),

    /**
     * The order below is reversed on purpose.
     * Revert it and will you get a NULL list of types furder.
     */

    I(EnumGrammar.THREE),
    H(EnumGrammar.TWO),
    F(EnumGrammar.ONE),
    E(EnumGrammar.ZERO);

    private String strValue;
    private EnumGrammar enumGrammarValue;

    private EnumTypes(String strValue) {
        this.strValue = strValue;
    }

    private EnumTypes(EnumGrammar enumGrammarValue) {
        this.enumGrammarValue = enumGrammarValue;
    }

    public String getStrValue() {
        return strValue;
    }

    public EnumGrammar getEnumTiposValue() {
        return enumGrammarValue;
    }
}
Run Code Online (Sandbox Code Playgroud)

EnumGrammar

public enum EnumGrammar {

    ZERO(EnumTypes.A,EnumTypes.B,EnumTypes.F,EnumTypes.D),
    ONE(EnumTypes.C),
    TWO(EnumTypes.B,EnumTypes.H),
    THREE(EnumTypes.D,EnumTypes.A,EnumTypes.C);

    private EnumTypes[] values;

    private EnumGrammar(EnumTypes ... values) {
        this.values = values;
    }

    public EnumTypes[] getValues() {
        return values;
    }
}
Run Code Online (Sandbox Code Playgroud)

当我调用的EnumTypes.E.getEnumTiposValue().getValues()地方应该是EnumTypes.F值为NULL.

主要

public class Main {

    public static void main(String[] args) {
        //prints [A, B, null, D]
        System.out.println(Arrays.toString(EnumTypes.E.getEnumTiposValue().getValues()));
    }

}
Run Code Online (Sandbox Code Playgroud)

有一种解决方法或类似的东西?

谢谢!

Mic*_*ski 11

从本质上讲,在完全构造类之前,即在构造函数完成之前,允许对象的引用进入类之外总是一件非常危险的事情.枚举是单身人士.这里有两个类,它们的构造函数以循环依赖关系接收彼此的实例.除此之外,类加载是惰性的,因此将加载类并在开始时创建枚举实例,并且结果结果取决于枚举初始化的顺序听起来非常合理.

我现在无法引用JLS中的相应点(我会寻找它),但我相信如果你允许引用一个对象从构造函数外部"离开类"(这是由于作为JVM初始化的单例的枚举,JVM可以自由地做一些奇怪的事情.

编辑:来自JLS的这些要点对于案例非常重要:

  • 17.5.2 - A read of a final field of an object within the thread that constructs that object is ordered with respect to the initialization of that field within the constructor by the usual happens-before rules. If the read occurs after the field is set in the constructor, it sees the value the final field is assigned, otherwise it sees the default value.由于枚举值在内部被视为静态最终字段(见下面的16.5),如果你从另一个构造函数引用第一个枚举的构造函数内部引用一个枚举,那么这两个对象中至少有一个还没有已完全初始化,因此此时引用可能仍为空.
  • 16.5 -The definite assignment/unassignment status of any construct within the class body of an enum constant is governed by the usual rules for classes
  • 8.3.2 - 字段初始化规则
  • 12.4.1 - 初始化时


Pau*_*ora 6

以下是按顺序发生的事情:

  1. 你的代码调用EnumTypes.E.getEnumTiposValue(),触发类加载EnumTypes.
  2. 静态初始化EnumTypes开始 - 它的枚举常量将按它们声明的顺序初始化.
  3. EnumTypes.A通过EnumTypes.D初始化.
  4. EnumTypes.I开始初始化 - 它的构造函数调用引用EnumGrammar.THREE,触发类加载EnumGrammar.
  5. 静态初始化EnumGrammar开始 - 它的枚举常量将按它们声明的顺序初始化.
  6. EnumGrammar.ZERO被初始化-它的构造函数调用的引用EnumTypes.A,EnumTypes.B,EnumTypes.F,和EnumTypes.D.其中,EnumTypes.F 尚未初始化.因此,对它的引用是null.

从那里开始,两个枚举类的静态初始化完成,但它没有关系EnumGrammar.ZERO- 它的values字段已经设置好了.