Enum的枚举为NULL

时间:2013-06-06 20:33:22

标签: java reference enums null

我正在为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;
    }
}

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

当我致电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()));
    }

}

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

谢谢!

3 个答案:

答案 0 :(得分: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),如果您从另一个构造函数引用第一个枚举的构造函数内部引用一个枚举,则at这两个对象中至少有一个尚未完全初始化,因此此时引用可能仍为空。
  • 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 - 初始化时

答案 1 :(得分:6)

以下是正在发生的事情:

  1. 您的代码调用EnumTypes.E.getEnumTiposValue(),触发EnumTypes的类加载。
  2. EnumTypes的静态初始化开始 - 它的枚举常量将按照它们声明的顺序初始化。
  3. EnumTypes.AEnumTypes.D已初始化。
  4. EnumTypes.I开始初始化 - 其构造函数调用引用EnumGrammar.THREE,触发EnumGrammar的类加载。
  5. EnumGrammar的静态初始化开始 - 它的枚举常量将按照它们声明的顺序初始化。
  6. EnumGrammar.ZERO已初始化 - 其构造函数调用引用EnumTypes.AEnumTypes.BEnumTypes.FEnumTypes.D。其中,EnumTypes.F 尚未初始化。因此,对它的引用是null
  7. 从那里开始,两个枚举类的静态初始化完成,但对EnumGrammar.ZERO无关紧要 - 它的values字段已经设置。

答案 2 :(得分:0)

对于变通方法,假设您有EnumA和EnumB,我将把EnumB的名称放在EnumA的构造函数中。

当您必须从EnumA检索EnumB时,您只需使用EnumB.valueOf(EnumA.this.enumB)

例如,问题是EnumB

public enum Question {
RICH_ENOUGH(R.string.question_rich_enough, Arrays.asList(Answer.RICH_ENOUGH_YES, Answer.RICH_ENOUGH_NO)),
ARE_YOU_SURE(R.string.question_are_you_sure, Arrays.asList(Answer.ARE_YOU_SURE_YES, Answer.ARE_YOU_SURE_NO)),
FOUND_A_NEW_JOB(R.string.question_found_new_job, Arrays.asList(Answer.FOUND_A_NEW_JOB_YES, Answer.FOUND_A_NEW_JOB_NO)),
// ...

和答案是EnumA

public enum Answer {
    RICH_ENOUGH_YES(R.string.answer_yes, "ARE_YOU_SURE"),
    RICH_ENOUGH_NO(R.string.answer_no, "THAT_SOMEBODY"),
    ARE_YOU_SURE_YES(R.string.answer_yes, null),
    ARE_YOU_SURE_NO(R.string.answer_no, "FOUND_A_NEW_JOB"),
    FOUND_A_NEW_JOB_YES(R.string.answer_yes, "GO_FOR_NEW_JOB"),
    // ...

    private final int answerStringRes;
    // Circular reference makes nulls
    private final String nextQuestionName;

    Answer(@StringRes int answerStringRes, String nexQuestionName) {
        this.answerStringRes = answerStringRes;
        this.nextQuestionName = nexQuestionName;
    }

每当我需要从答案中获得下一个问题时

public Question getNextQuestion() {
    if (nextQuestionName == null) {
        return null;
    }
    return Question.valueOf(nextQuestionName);
}

这应该很简单,可以成为一种解决方法。

示例来源:我刚刚写的一个开源Android应用程序,我昨晚写了Should I Resign?