Java Enums
Two interesting insights about Java enums that I found today that are not obvious and might be useful for others.
Enums with methods
Sometimes it can be useful to associate certain behaviour to an enum constant. For example, today I wrote an enum that represents the different types of images of the X protocol:
public enum Format {
BITMAP, XYPIXMAP, ZPIXMAP
}
Later in the protocol implementation I needed the IDs that are associated with each format. My first implementation used a switch statement (see below) to determine the ID. However, there’s a more elegant way to implement this:
public static enum Format {
BITMAP { public int id() { return 0; } },
XYPIXMAP { public int id() { return 1; } },
ZPIXMAP { public int id() { return 2; } };
public abstract int id ();
}
Ok, you can say that I could have used the ordinal() method of the enum. However, the above is more powerful. You can implement arbitrarily complex methods (with parameters etc) in an enum. I would tend to be concise though, and not implement anything that doesn’t fit on one or two lines.
Enums in switch statements
Well, this is of course a central design aspect of enums. I found it a little difficult though, until I found the correct syntax. What I did is that:
switch (format) {
case Image.Format.BITMAP: // blah
case Image.Format.XYPIXMAP: // blah
case Image.Format.ZPIXMAP // blah
}
Looks like an obvious approach right? Well the compiler didn’t like it. It took me a while and some hints by friends on IRC to find out what’s correct:
switch (format) {
case BITMAP: // blah
case XYPIXMAP: // blah
case ZPIXMAP: // blah
}
The case labels ‘inherit’ the type from the object passed into the switch statement. Well, now that I think about it it is obvious, because it doesn’t make much sense to switch over something like:
switch (format) {
case Image.Format.BITMAP: // blah
case Image.Format.XYPIXMAP: // blah
case Image.Format.ZPIXMAP // blah
case Something.OTHER: // Bad!
}
So it makes sense for the compiler to not accept such code in the first place. I still had some trouble and maybe this post saves somebody some time.
July 13th, 2007 at 10:29 pm
Your id() approach is a good demonstration of how methods in enums generally work. But readers should be aware that for that particular case, you’re really only reimplementing ordinal(), functionality which is inherent to and free with enums.
July 13th, 2007 at 10:35 pm
Haakon, yes you’re right. But in this particular case, the ID (in the X protocol) really hasn’t much to do on the ordinal of the enum. If I ordered them differently, the IDs still must be those that I specified here. Of course, I could have coded up a more useful example, but I was lazy and copy+pasted my code.
July 14th, 2007 at 11:08 pm
I see what you mean, and you’re right, of course.
By the way, here is a different solution to the same case. It’s less powerful than yours with abstract method implementation per member, but perhaps easier to read if it is clear that the getter implementation will be the same for all cases.
public enum Format {
BITMAP(0),
XYPIXMAP(1),
ZPIXMAP(2);
private int _id;
Format(int id) { _id = id; }
public int id() { return _id; }
}
(Hope the formatting went well)
July 16th, 2007 at 10:37 pm
I personally really like enums, and will really like to see more of them.
By the way, one issue you need to be aware of, is that ordinal() and name() of an enum is not a constant (or literal) for the compiler.
What I mean is that “Format.BITMAP.ordinal()” is not equivalent to “public static int BITMAP = 0;”. But, in fact internally it is totally equivalent since Format.BITMAP is “public static Format BITMAP = new Format(0,”BITMAP”);”.
That one reason you cannot use ordinal() and name() in annotation for example.