Bootstrapping static fields within enums

In my earlier post on enums in Java 5, we have seen that static fields cannot be accessed within the enum constructor. With this restriction we could run into few initialization problems discussed below.

The other day Rajiv found it difficult to initialize a static cache during enum bootstrap. The following is his enum:

public enum Tag {
    KEYWORDS(2, 25, true),
    DATE_CREATED(2, 55, false),
    HEADLINE(2, 105, false),
    …
    private int recordNo;
    private int datasetNo;
    private int isRepeat;

    private Tag(int recordNo, int datasetNo, int isRepeat) {
        this.recordNo = recordNo;
        this.datasetNo = datasetNo;
        this.isRepeat = isRepeat;
    }
    …
    public static Tag getTag(int recordNo, int datasetNo) {
        for(Tag tag : Tag.values()) { //$REVIEW$ optimize
            if(tag.getRecordNo() == recordNo && tag.getDatasetNo() == datasetNo){
                return tag;
            }
        }
    }
}

Rajiv says, “Deeps, check my implementation of getTag(int recordNo, int datasetNo). I would have liked to make a static map of (recordNo<<8 + datasetNo) vs Tag to make this method fast. Unfortunately, I cannot access that static map from the constructor.”

So, he would like to have a static cache of enums for easy retrieval based on their properties. But, the concern is where do we initialize this cache/map? In the discussion following the earlier post, we discussed that “enums are initialized before any static initializers are run”. Bang! We can initialize this cache in a static block as shown below (actually derived from Neal Gafter’s discussion on forum).

public enum Tag {
    KEYWORDS(2, 25, true),
    DATE_CREATED(2, 55, false),
    HEADLINE(2, 105, false),
    …
    private static Map<Integer, Tag> cache;
    static {
        cache = new HashMap<Integer, Tag>();
        for(Tag tag : values()) {
            int key = tag.getRecordNo()<<8 + tag.getDatasetNo();
            cache.put(key, tag);
        }
    }
    …
    public static Tag getTag(int recordNo, int datasetNo) {
        int key = recordNo<<8 + datasetNo;
        return cache.get(key);
    }
}

Enum constants are already constructed, by the time static block initializer is run. This is a ridiculous pattern to follow though, when you need to boot strap your static fields within enums.

Advertisements

About Deepak Anupalli
Deepak Anupalli is a lead developer and performance expert at Pramati Server Engineering Group.

7 Responses to Bootstrapping static fields within enums

  1. Benedetto says:

    “enums are initialized before any static initializers are run”, so you can initialize the static fields during enum initialization (:in the enum constructor):

    public enum Tag{
    ….
    private static Map cache;

    private Tag(int recordNo, int datasetNo, int isRepeat){
    //initialize statics–
    if cache==null
    cache=new TreeMap();
    //–
    this.recordNo = recordNo;
    this.datasetNo = datasetNo;
    this.isRepeat = isRepeat;
    cache.put(this.getRecordNo()<<8 + this.getDatasetNo(), this);
    }

    }

    it looks odd too but maybe less odd than the for each static block.

  2. VE says:

    I think its fair to expect such a behavior for enums from SUN. There will be always people who wants to use enums in the static objects so creation of enums first makes sense. Also another school might think of using static fields in creation of enums. I assume SUN has taken the former over the later

  3. Benita says:

    Benedetto: Using Eclipse 3.4, your code give the following compile error:
    Cannot refer to the static enum field Tag.cache within an initializer

  4. Benedetto says:

    to Benita: Yes, i missed that part of the article was right about static fields not being accessible from the enum initializer.
    Anyway you can still avoid the static for each bootstrap and let each enum constant to cache itself at initialization time by moving the cache into a static method that will also instantiate the cache:

    private static Map getCache(){
    if (cache==null)
    cache=new LinkedHashMap();
    return cache;
    }

    private Tag(int recordNo, int datasetNo, boolean isRepeat){
    //initialize statics–
    //–
    this.recordNo = recordNo;
    this.datasetNo = datasetNo;
    this.isRepeat = isRepeat;
    getCache().put(this.getRecordNo()<<8 + this.getDatasetNo(), this);
    }

  5. snow says:

    Hello, I have found your article and I was hoping you may be able to explain what the problem is in the piece of code that will follow if there is one, because I am the only one in my team that gets it. Is it a Java problem or some Eclipse problem/setting.
    Thanks for your help.

    public enum MyClass {
    // …
    private static int nextIndex = 0;
    private final int index = MyClass.nextIndex++;
    /*only I get the error “Cannot refer to the static enum field MyClass.nextIndex within an initializer”*/
    // …
    }

  6. Hi,

    This is an interesting case, where you can access a static field while the Enum instance is being constructed though not directly through the constructor.

    I tried to compile this piece of code on JDK 1.5 and 1.6 and found that it works perfectly fine with 1.5 and compilation fails with 1.6. Probably, you are trying to compile with 1.6.

    Looks like they have got away with this in JDK 1.5 and fixed it in 1.6. Otherwise, this case would have been an anomaly to my post ;). Thanks for the use-case.

  7. Pingback: Enum and other Java 5 tricks …

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: