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);
}
}</integer,></integer,>
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.
“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.
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
Benedetto: Using Eclipse 3.4, your code give the following compile error:
Cannot refer to the static enum field Tag.cache within an initializer
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);
}
…
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”*/
// …
}
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.