Mutant World

Sunday, July 27, 2008

Static Fields in Enums

Enums in Java have a numeric, read-only, property called ordinal that is a strictly consecutive integer (starting from 0).

Sometimes, however, it is necessary to associate to an Enum another numeric property, let's call it code, that it may not be strictly consecutive and normally specifies something different than a sequence number. Furthermore, it may be necessary to be able to lookup the correct Enum from its code.

It seems totally trivial to write an Enum that has a static map from codes to Enum instances:

public enum Port
{
SSH(22), TELNET(23), HTTP(80), POP3(110), HTTPS(443);

// The static map from codes to Enum instances
private static final
Map<Integer, Port> ports = new HashMap<Integer, Port>();
private final int code;

private Port(int code)
{
this.code = code;
ports.put(code, this);
}

public int getCode()
{
return this.code;
}

// Lookup method that returns the Enum instance from the given code
public static Port from(int code)
{
return ports.get(code);
}
}

Note the method static from(int code) that returns an Enum instance from the given code.

Unfortunately, this will not compile. The compiler reports that it is illegal to access the static member ports from the constructor.

This makes sense when one imagines how an Enum is first translated into a class by the compiler. Roughly, there is a static initializer that creates the Ports instances, in this case SSH, TELNET, etc.; this static initializer is the first initializer run when the Ports class is referenced, and it is easy to see that when the static initializer runs, no other initializers have been run yet, and in particular the static Map ports has not been created yet.
See http://java.sun.com/docs/books/jls/third_edition/html/classes.html#301020 for further details.

Fortunately, there is an easy workaround: it's enough to use a different class to store the map from codes to Enum instances, and a private static inner class does the job:

public enum Port
{
SSH(22), TELNET(23), HTTP(80), POP3(110), HTTPS(443);

private final int code;

private Port(int code)
{
this.code = code;
Ports.ports.put(code, this);
}

public int getCode()
{
return this.code;
}

// Lookup method that returns the Enum instance from the given code
public static Port from(int code)
{
return Ports.ports.get(code);
}

private static class Ports
{
// The static map from codes to Enum instances
private static final
Map<Integer, Port> ports = new HashMap<Integer, Port>();
}
}

Labels: ,

Randy Pausch Last Lecture

I just watched Randy Pausch last lecture, and found it absolutely incredible, one of those gems that cannot be missed.

If you want to spend 1hr 16 mins in something really worth, then go watch it.

My favorite quote:
"The brickwalls are there for a reason: to give us a chance to show how badly we want something. Brickwalls are there to stop the people who don't want it badly enough."

Thanks Randy: for the head fake that I've got from your last lecture.