tags:

views:

754

answers:

2

Why can't enum's constructor access static fields and methods? This is perfectly valid with a class, but is not allowed with an enum.

What I'm trying to do is store my enum instances in a static Map. Consider this example code which allows lookup by 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);
    }
}

This will not work as enum doesn't allow static references in its constructor. It however works just find if implemented as a class:

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
}
+11  A: 

The constructor is called before the static fields have all been initialized, because the static fields (including those representing the enum values) are initialized in textual order, and the enum values always come before the other fields. Note that in your class example you haven't shown where ABBREV_MAP is initialized - if it's after SUNDAY, you'll get an exception when the class is initialized.

Yes, it's a bit of a pain and could probably have been designed better.

However, the usual answer in my experience is to have a static {} block at the end of all the static initializers, and do all static initialization there, using EnumSet.allOf to get at all the values.

Jon Skeet
If you add a nested class, then the statics of that will be initialised at an appropriate time.
Tom Hawtin - tackline
Ooh, nice one. I hadn't thought of that.
Jon Skeet
I don't really understand the statement about the static initializer block. I tried creating a static initializer block in an enum class, and it was being called _after_ the constructor of the enum member, apparently done according to textual order as you said.
Hosam Aly
If you try to use a static variable within the enum constructor (as in the question) you'll see the uninitialized value. Instead, use a static initializer to initialize everything *after* the enum values have all been constructed and the rest of the static variables are initialized.
Jon Skeet
Thanks for the clarification Jon.
Hosam Aly
A: 

Jon Skeet's answer is very complete, although i will add that you can just use <enumClassName>.values() to get all the enum values.

james
I don't want all the values, but a particular instance based on a key.
Steve Kuo