Java 为什么枚举的构造函数不能访问静态字段?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/443980/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why can't enum's constructor access static fields?
提问by Steve Kuo
Why can't enum's constructor access static fields and methods? This is perfectly valid with a class, but is not allowed with an enum.
为什么枚举的构造函数不能访问静态字段和方法?这对类完全有效,但不允许用于枚举。
What I'm trying to do is store my enum instances in a static Map. Consider this example code which allows lookup by abbreivation:
我想要做的是将我的枚举实例存储在静态 Map 中。考虑这个允许按缩写查找的示例代码:
public enum Day {
Sunday("Sun"), Monday("Mon"), Tuesday("Tue"), Wednesday("Wed"), Thursday("Thu"), Friday("Fri"), Saturday("Sat");
private final String abbreviation;
private static final Map<String, Day> ABBREV_MAP = new HashMap<String, Day>();
private Day(String abbreviation) {
this.abbreviation = abbreviation;
ABBREV_MAP.put(abbreviation, this); // Not valid
}
public String getAbbreviation() {
return abbreviation;
}
public static Day getByAbbreviation(String abbreviation) {
return ABBREV_MAP.get(abbreviation);
}
}
This will not work as enum doesn't allow static references in its constructor. It however works just find if implemented as a class:
这将不起作用,因为枚举不允许在其构造函数中使用静态引用。然而,它只是查找是否作为类实现:
public static final Day SUNDAY = new Day("Sunday", "Sun");
private Day(String name, String abbreviation) {
this.name = name;
this.abbreviation = abbreviation;
ABBREV_MAP.put(abbreviation, this); // Valid
}
采纳答案by Jon Skeet
The constructor is called before the static fields have all been initialized, because the static fields (including those representing the enum values) are initialized in textual order, and the enum values always come before the other fields. Note that in your class example you haven't shown where ABBREV_MAP is initialized - if it's afterSUNDAY, you'll get an exception when the class is initialized.
在静态字段全部初始化之前调用构造函数,因为静态字段(包括表示枚举值的那些)是按文本顺序初始化的,并且枚举值总是在其他字段之前。请注意,在您的类示例中,您没有显示 ABBREV_MAP 的初始化位置 - 如果在SUNDAY之后,您将在类初始化时收到异常。
Yes, it's a bit of a pain and could probably have been designed better.
是的,这有点痛苦,可能设计得更好。
However, the usual answer in my experience is to have a static {}
block at the end of all the static initializers, and do all static initialization there, using EnumSet.allOf
to get at all the values.
但是,根据我的经验,通常的答案是static {}
在所有静态初始化程序的末尾有一个块,并在那里进行所有静态初始化,EnumSet.allOf
用于获取所有值。
回答by Phani
Quote from JLS, section "Enum Body Declarations":
Without this rule, apparently reasonable code would fail at run time due to the initialization circularity inherent in enum types. (A circularity exists in any class with a "self-typed" static field.) Here is an example of the sort of code that would fail:
enum Color { RED, GREEN, BLUE; static final Map<String,Color> colorMap = new HashMap<String,Color>(); Color() { colorMap.put(toString(), this); } }
Static initialization of this enum type would throw a NullPointerExceptionbecause the static variable colorMap is uninitialized when the constructors for the enum constants run. The restriction above ensures that such code won't compile.
Note that the example can easily be refactored to work properly:
enum Color { RED, GREEN, BLUE; static final Map<String,Color> colorMap = new HashMap<String,Color>(); static { for (Color c : Color.values()) colorMap.put(c.toString(), c); } }
The refactored version is clearly correct, as static initialization occurs top to bottom.
如果没有这条规则,由于枚举类型固有的初始化循环,显然合理的代码将在运行时失败。(任何具有“自类型”静态字段的类中都存在循环。)这是会失败的代码类型的示例:
enum Color { RED, GREEN, BLUE; static final Map<String,Color> colorMap = new HashMap<String,Color>(); Color() { colorMap.put(toString(), this); } }
此枚举类型的静态初始化将抛出NullPointerException,因为当枚举常量的构造函数运行时静态变量 colorMap 未初始化。上述限制确保此类代码不会编译。
请注意,该示例可以轻松重构以正常工作:
enum Color { RED, GREEN, BLUE; static final Map<String,Color> colorMap = new HashMap<String,Color>(); static { for (Color c : Color.values()) colorMap.put(c.toString(), c); } }
重构版本显然是正确的,因为静态初始化从上到下发生。
回答by Hitesh
When a class is loaded in the JVM then static fields are initialized in the order in which they appear in code. For e.g.
当一个类被加载到 JVM 中时,静态字段将按照它们在代码中出现的顺序进行初始化。例如
public class Test4 {
private static final Test4 test4 = new Test4();
private static int j = 6;
Test4() {
System.out.println(j);
}
private static void test() {
}
public static void main(String[] args) {
Test4.test();
}
}
The output will be 0. Note that test4 initialization takes place in static initialization process and during this time j is not yet initialized as it appears later. Now if we switch order of static initializers such that j comes before test4. The output will be 6.But in case of Enums we cannot alter order of static fields. The first thing in enum must be the constants which are actually static final instances of enum type.Thus for enums its always guaranteed that static fields wont be initialized before enum constants.Since we cannot give any sensible values to static fields for use in enum constructor, it would be meaningless to access them in enum constructor.
输出将为 0。请注意,test4 初始化发生在静态初始化过程中,并且在此期间 j 尚未初始化,因为它稍后会出现。现在,如果我们切换静态初始值设定项的顺序,使 j 出现在 test4 之前。输出将是 6.但在枚举的情况下,我们不能改变静态字段的顺序。枚举中的第一件事必须是常量,它们实际上是枚举类型的静态最终实例。因此,对于枚举,它始终保证静态字段不会在枚举常量之前初始化。因为我们无法为用于枚举构造函数的静态字段提供任何合理的值,在枚举构造函数中访问它们是没有意义的。
回答by Pavel Vlasov
The problem solved via a nested class. Pros: it's shorter and also better by CPU consumption. Cons: one more class in JVM memory.
该问题通过嵌套类解决。优点:它更短,CPU 消耗也更好。缺点:JVM 内存中多了一个类。
enum Day {
private static final class Helper {
static Map<String,Day> ABBR_TO_ENUM = new HashMap<>();
}
Day(String abbr) {
this.abbr = abbr;
Helper.ABBR_TO_ENUM.put(abbr, this);
}
public static Day getByAbbreviation(String abbr) {
return Helper.ABBR_TO_ENUM.get(abbr);
}
回答by user4767902
maybe this is what you want
也许这就是你想要的
public enum Day {
Sunday("Sun"),
Monday("Mon"),
Tuesday("Tue"),
Wednesday("Wed"),
Thursday("Thu"),
Friday("Fri"),
Saturday("Sat");
private static final Map<String, Day> ELEMENTS;
static {
Map<String, Day> elements = new HashMap<String, Day>();
for (Day value : values()) {
elements.put(value.element(), value);
}
ELEMENTS = Collections.unmodifiableMap(elements);
}
private final String abbr;
Day(String abbr) {
this.abbr = abbr;
}
public String element() {
return this.abbr;
}
public static Day elementOf(String abbr) {
return ELEMENTS.get(abbr);
}
}