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