11

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

枚举类型

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

枚举语法

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()where 应该是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()));
    }

}

有解决方法或类似的方法吗?

谢谢!

4

3 回答 3

12

从本质上讲,在类完全构造之前,即在构造函数完成之前,允许对对象的引用离开类始终是一件非常危险的事情。枚举是单例。这里有两个类,它们的构造函数以循环依赖的形式接收彼此的实例。再加上类加载是惰性的,因此将加载类并创建枚举实例,最终结果取决于初始化枚举的顺序,这听起来很合理。

我现在不能引用 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 - 初始化发生时
于 2013-06-06T20:50:42.597 回答
7

这是正在发生的事情,按顺序:

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

从那里,两个枚举类的静态初始化完成,但这并不重要EnumGrammar.ZERO- 它的values字段已经设置。

于 2013-06-06T20:54:13.490 回答
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 应用程序 -我应该辞职吗?

于 2015-10-23T04:51:44.873 回答