为什么枚举的构造函数不能访问静态字段?

问题描述 投票:0回答:6

为什么枚举的构造函数不能访问静态字段和方法?这对于类来说是完全有效的,但对于枚举来说是不允许的。

我想做的是将枚举实例存储在静态映射中。考虑这个示例代码,它允许通过缩写查找:

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

这不起作用,因为枚举不允许在其构造函数中进行静态引用。然而,如果作为一个类实现,它就可以找到:

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
}
java enums
6个回答
127
投票

在静态字段全部初始化之前调用构造函数,因为静态字段(包括表示枚举值的字段)是按文本顺序初始化的,并且枚举值始终位于其他字段之前。请注意,在您的类示例中,您没有显示 ABBREV_MAP 的初始化位置 - 如果是在周日之后,则在类初始化时您会收到异常。 是的,这有点痛苦,可能可以设计得更好。

但是,根据我的经验,通常的答案是在所有静态初始化程序的末尾有一个

static {}

块,并在那里进行所有静态初始化,使用

EnumSet.allOf
获取所有值。
    


43
投票
JLS,“枚举主体声明”部分

如果没有这条规则,显然合理的代码将在运行时失败 由于枚举类型固有的初始化循环性。 (A 循环性存在于任何具有“自类型”静态字段的类中。) 这是会失败的代码类型的示例:

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

重构版本显然是正确的,因为静态初始化是从上到下发生的。


12
投票

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



9
投票

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



1
投票

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。但是对于枚举,我们无法更改静态字段的顺序。枚举中的第一件事必须是常量,它们实际上是枚举类型的静态最终实例。因此,对于枚举,它总是保证静态字段不会在枚举常量之前初始化。因为我们不能为静态字段提供任何合理的值以在枚举构造函数中使用,在枚举构造函数中访问它们是没有意义的。


0
投票

使用静态启动的映射并使用该映射的字符串键调用枚举构造函数。

enum MyEnum { ONE('KEY_1'), TWO('KEY_2') private static final Map<String, Double> PRIVATE_MAP static { double someUsefulCalculationInAStaticContextToInitializeTheMap = 0.8 PRIVATE_MAP = Collections.unmodifiableMap([ KEY_1: someUsefulCalculationInAStaticContextToInitializeTheMap * 0.20, KEY_2: someUsefulCalculationInAStaticContextToInitializeTheMap * 1.2 ]) } private final String mapKey private MyEnum(String mapKey) { this.mapKey = mapKey } Double getMyEnumProperty() { PRIVATE_MAP[mapKey] } }

© www.soinside.com 2019 - 2024. All rights reserved.