Enum的枚举为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())); } } 

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

谢谢!

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

我现在无法引用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. 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 – 初始化时

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

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

从那里开始,两个枚举类的静态初始化完成,但是对于EnumGrammar.ZERO并不重要 – 它的values字段已经设置好了。

对于解决方法,假设您有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应用程序,我昨晚写的很有趣 – 我应该辞职吗?