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

时间:2009-01-14 17:45:31

标签: java enums

为什么enum的构造函数不能访问静态字段和方法?这对于类非常有效,但不允许使用枚举。

我要做的是将我的枚举实例存储在静态Map中。考虑这个允许通过abbreivation进行查找的示例代码:

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
}

5 个答案:

答案 0 :(得分:105)

在静态字段全部初始化之前调用构造函数,因为静态字段(包括表示枚举值的字段)按文本顺序初始化,并且枚举值始终位于其他字段之前。请注意,在您的类示例中,您尚未显示ABBREV_MAP初始化的位置 - 如果 SUNDAY之后,则在初始化类时会出现异常。

是的,这有点痛苦,可能设计得更好。

但是,根据我的经验,通常的答案是在所有静态初始值设定项的末尾都有一个static {}块,并在那里进行所有静态初始化,使用EnumSet.allOf来获取所有值。

答案 1 :(得分:29)

引自JLS, section "Enum Body Declarations"

  

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

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

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

答案 2 :(得分:7)

也许这就是你想要的

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

答案 3 :(得分:6)

通过嵌套类解决问题。优点:它的速度更短,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);
    }

答案 4 :(得分:1)

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