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


110

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

我正在尝试将枚举实例存储在静态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
}

Answers:


113

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

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

但是,根据我的经验,通常的答案是static {}在所有静态初始化程序的末尾添加一个块,然后在其中进行所有静态初始化,EnumSet.allOf 以获取所有值。


40
如果添加嵌套类,则将在适当的时间初始化该类的静态对象。
Tom Hawtin-大头钉

哦,很好。我没想到。
乔恩·斯基特

3
有点奇怪,但是如果您在枚举构造函数中调用静态方法,该方法返回静态值,则可以正常编译-但返回的值将是该类型的默认值(即0、0.0,'\ u0000'或即使您明确设置了它(除非将其声明为final)。猜猜这将是一个很难抓住的!
马克·罗德斯

2
快速衍生问题@JonSkeet:您使用EnumSet.allOf代替的任何理由Enum.values()?我问是因为这values是一种幻影方法(在中看不到源代码Enum.class),我也不知道它是何时创建的
-Chirlo 2012年

1
@Chirlo有一个问题Enum.values()如果您计划使用增强的for循环遍历它们(因为它返回一个数组),则似乎会更快,但是主要是关于样式和用例。EnumSet.allOf()如果您想编写Java文档中存在的代码而不是仅在规范中使用,可能会更好,但是Enum.values()无论如何,似乎很多人都很熟悉。
6

31

引用JLS,“枚举主体声明”部分

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

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

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


9

也许这就是你想要的

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

Collections.unmodifiableMap()在这里使用是一个很好的做法。+1
城堡

正是我想要的。我也喜欢看到Collections.unmodifiableMap。谢谢!
LethalLima

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

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,但是对于枚举,我们无法更改静态字段的顺序。枚举中的第一件事必须是常量,它们实际上是枚举类型的静态最终实例。因此,对于枚举,它始终保证静态字段不会在枚举常量之前初始化。 ,在enum构造函数中访问它们将毫无意义。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.