Java 为什么可以';t枚举';如何访问静态字段?
为什么枚举的构造函数不能访问静态字段和方法?这对于类是完全有效的,但对于枚举是不允许的 我要做的是将枚举实例存储在静态映射中。考虑这个示例代码,它允许通过缩写查找:Java 为什么可以';t枚举';如何访问静态字段?,java,enums,Java,Enums,为什么枚举的构造函数不能访问静态字段和方法?这对于类是完全有效的,但对于枚举是不允许的 我要做的是将枚举实例存储在静态映射中。考虑这个示例代码,它允许通过缩写查找: public enum Day { Sunday("Sun"), Monday("Mon"), Tuesday("Tue"), Wednesday("Wed"), Thursday("Thu"), Friday("Fri"), Saturday("Sat"); private final String abbrev
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);
}
}
在静态字段全部初始化之前调用构造函数,因为静态字段(包括表示枚举值的字段)是按文本顺序初始化的,并且枚举值始终位于其他字段之前。请注意,在您的类示例中,您没有显示初始化ABBREV_映射的位置-如果是在星期日之后,则在初始化该类时会出现异常 是的,这有点痛苦,可能设计得更好 然而,根据我的经验,通常的答案是在所有静态初始值设定项的末尾有一个
static{}
块,并在那里进行所有静态初始化,使用EnumSet.allOf
获取所有值。引用自:
如果没有这个规则,显然合理的代码将在运行时失败
由于枚举类型固有的初始化循环性。(一)
循环性存在于任何具有“自类型”静态字段的类中。)
下面是一个失败代码的示例:
enum Color {
RED, GREEN, BLUE;
static final Map<String,Color> colorMap = new HashMap<String,Color>();
Color() {
colorMap.put(toString(), this);
}
}
枚举颜色{
红、绿、蓝;
静态最终映射colorMap=newhashmap();
颜色(){
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);
}
}
枚举颜色{
红、绿、蓝;
静态最终映射colorMap=newhashmap();
静止的{
对于(颜色c:Color.values())
colorMap.put(c.toString(),c);
}
}
重构后的版本显然是正确的,因为静态初始化是自上而下进行的
在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();
}
}
输出将为0。请注意,test4初始化发生在静态初始化过程中,在这段时间内,j还没有像后面显示的那样初始化。现在,如果我们切换静态初始值设定项的顺序,使得j位于test4之前。输出将是6。但是在枚举的情况下,我们不能改变静态字段的顺序。enum中的第一件事必须是常量,它实际上是enum类型的静态最终实例。因此,对于enum,它始终保证静态字段不会在enum常量之前初始化。因为我们无法为静态字段提供任何合理的值,以便在enum构造函数中使用,在枚举构造函数中访问它们是没有意义的。问题通过嵌套类解决。优点:它更短,而且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);
}
枚举日{
私有静态最终类帮助器{
静态映射ABBR_TO_ENUM=new HashMap();
}
日(字符串缩写){
this.abbr=abbr;
Helper.ABBR_TO_ENUM.put(ABBR,this);
}
公共静态日GetBy缩写(字符串缩写){
将Helper.ABBR_返回到_ENUM.get(ABBR);
}
也许这就是你想要的
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);
}
}
公共枚举日{
星期日(“太阳”),
星期一(星期一),
星期二(星期二),
星期三(星期三),
星期四(星期四),
星期五(星期五),
星期六(星期六);
私有静态最终映射元素;
静止的{
Map elements=newhashmap();
对于(日值:values()){
elements.put(value.element(),value);
}
元素=集合。不可修改的映射(元素);
}
私有最终字符串缩写;
日(字符串缩写){
this.abbr=abbr;
}
公共字符串元素(){
返回这个.abbr;
}
公共静态日元素(字符串缩写){
返回元素.get(缩写);
}
}
如果您添加一个嵌套类,那么该类的静态将在适当的时间初始化。哦,很好的一个。我没有想到这一点。有点奇怪,但是如果您在枚举构造函数中调用一个静态方法,该方法返回一个静态值,它将很好地编译-但它返回的值将是该类型的默认值(即0、0.0、'\u0000'或null),即使您显式地设置它(除非声明为final
)。我猜这将是一个很难理解的问题!快速衍生问题@JonSkeet:您使用EnumSet.allOf
而不是Enum.values()
,有什么原因吗?我这样问是因为值是一种幻影方法(在Enum.class
中看不到源代码)我也不知道它是什么时候出现的created@Chirlo关于这一点,有一个问题。如果您计划使用增强的for循环(因为它返回一个数组)对它们进行迭代,Enum.values()
似乎更快,但主要是关于样式和用例。使用EnumSet.allOf()可能更好
如果您想编写Java文档中的代码,而不仅仅是规范中的代码,但许多人似乎都熟悉Enum.values()
无论如何。使用Collections.unmodifiableMap()
在这里是一个非常好的实践。+1这正是我想要的。我也喜欢看Collections.unmodifiableMap。谢谢!正如所描述的,我们有这个变通日(String…缩写){for.ABBR.put(name(),this);for(String ABBR:缩写){for.ABBR.put(ABBR,this);}//解决{private static final Map ABBR=new HashMap();}的私有静态类问题
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);
}
}