Recently, I ran into an interesting bit of code related to Java enumerations. Here’s a contrived minimal sample:
/*
* Some arbitrary resource type
*/
interface Resource {
public Resource getResource();
}
enum A implements Resource{
ALPHA (B.BRAVO);
private Resource res;
private A (Resource res){ this.res = res; }
public Resource getResource(){ return res; }
}
enum B implements Resource{
BRAVO (C.CHARLIE);
private Resource res;
private B (Resource res){ this.res = res; }
public Resource getResource(){ return res; }
}
enum C implements Resource{
CHARLIE (A.ALPHA);
private Resource res;
private C (Resource res){ this.res = res; }
public Resource getResource(){ return res; }
}
class EnumTester{
public static void main(String [] args){
System.out.println(A.ALPHA.getResource());
System.out.println(B.BRAVO.getResource());
System.out.println(C.CHARLIE.getResource());
}
}
When this code is run, an intuitive expectation is to see the following printed to the console:
BRAVO CHARLIE ALPHA
However, when I actually run the code we see:
BRAVO CHARLIE null
While this may seem a bit funky, the key is to understand what enum constants mean in Java. The key question to address is: “when is the constructor for an enumerated type invoked”. To answer this, it is helpful to remember that enum constants are implicitly public static final. Java guarantees that:
- Static variables in a class are initialized before any object of that class can be created
- Static variables in a class are initialized before any static method of the class runs
Knowing this, the behavior of the above code snippet becomes clearer. Here’s what happens in the main method:
- The fun happens at line 35, when we print A.ALPHA.getResource(). Behind the scene, the class loader loads enumeration A into memory (i.e. initializes A).
- Once A is loaded into memory, the enum constant ALPHA gets created via call to the constructor where B.BRAVO is passed in as the argument.
- At this point, enumeration B is initialized and enum constant BRAVO gets created via a call to the constructor where C.CHARLIE is passed in as the argument.
- Same as before, now enumeration C is initialized (i.e. loaded into memory). Now here’s where things become interesting.
- Immediately after the initialization of C, we create the enum constant CHARLIE via a call to the constructor and pass in A.ALPHA as the argument. However, A.ALPHA has not been instantiated yet, and therefore enum constant CHARLIE gets null as the value of its resource instance variable.
This means that once the statement A.ALPHA.getResource() is evaluated, A, B and C are all loaded into memory and all the corresponding enum constants have already been instantiated. Lines 35-37 simply then print the corresponding resources for each enum constant.
A Possible Solution
It is obvious that the entire problem stems from the cyclic dependency between A, B and C. Usually (but not always), this is an indication of a bad design choice and the fix is to simply get rid of the cyclic dependency. Assuming we want to keep the dependency, what are our options then?
Our solutions lies in Java’s ability to allow instance-specific methods on enumerations. This is shown in the code below.
Running the following code with the EnumTester main method above will give us the results we wanted, without the problems caused by the previous approach.
enum A implements Resource{
ALPHA { public Resource getResource() {return B.BRAVO; } };
public abstract Resource getResource();
}
enum B implements Resource{
BRAVO { public Resource getResource() { return C.CHARLIE; } };
public abstract Resource getResource();
}
enum C implements Resource{
CHARLIE { public Resource getResource() { return A.ALPHA; }};
public abstract Resource getResource();
}
What do you think? Do you know of any other ways to resolve the cyclic dependency? Have you seen any other weirdness related to Java’s Enums?
It seems like in this code Resource is actually a ResourceContainer – that looks suspecious and may suggest that different design could solve the problem.
I think that this is more of design problem than Java issue – why would you need such a dependency? Can’t you resolve it by using other objects?
In essence you are trying to something like that:
public static final String A = new String(B);
public static final String B = new String(C);
public static final String C = new String(A);
I absolutely agree that for a vast majority of the cases this is a design issue, and the problem will be fixed with a different (and better) design.
My point simply was that there may be a scenario where the design cannot be changed (for some reason). I’ve tried to highlight a solution that would work for such a particular case.
You can do this in Eclipse AFAIK. I’ve run into this issue before with SUN’s compiler which does not allow simple modeling of a state machine using Enum (through vararg constructor):
PlayerState.PLAY(STOP, PAUSE), PlayerState.STOP(PLAY),…