Tuesday, November 24, 2009

Java Enums with constant-specific methods

One of my colleagues introduced me to this handy language feature of Java 1.5 and I wanted to write an article about it because I hadn't seen this before.

Using google reveals that it is already well documented if you RTFM, but I will repeat it here because talking to other Java developers indicates that it isn't as well known as it deserves to be. Here's a slightly reworded extract from the first hit on google or bing for "java enum":

You can declare abstract methods in an enum and override them with a concrete method in each constant. Such methods are known as constant-specific methods. Here is an example using this technique:

public enum Operation {
PLUS { double eval(double x, double y) { return x + y; } },
MINUS { double eval(double x, double y) { return x - y; } },
TIMES { double eval(double x, double y) { return x * y; } },
DIVIDE { double eval(double x, double y) { return x / y; } };

// Do arithmetic op represented by this constant
abstract double eval(double x, double y);
}

Copyright © 2009 Ivan Moore

2 comments:

dirty said...

Although this is very useful (I'm a big fan of that polymorphism stuff myself), you have to remember that you are effectively creating an anonymous extension of the actual enum class.

ie. Printing out the class name of PLUS would result in:

Operation$1

This can cause issues such as if you attempt to serialise/deserialise it. We encountered this issue over a Hessian connection if I remember correctly.

This problem can be overcome by un-abstracting the object method and passing a Closure-like function into the constructor which is called in the now concrete method. Slightly uglier, but it works... :)

Who? said...

I'm big fan of this approach too - (you don't need java 1.5 enums, it works the with old type-safe-enum class pattern).

There's a rule of thumb I find useful for deciding when polymorphism is better than the alternative case statement.

With strategy-enums like this you have 2 considerations, the number of instances and the number of methods. Or more pertinently how these might grow.

Consider the alternative :
public enum Operation {
PLUS, MINUS, TIMES, DIVIDE;

double eval(double x, double y) {
switch (this) {
case PLUS :
return x + y;
case MINUS :
return x - y;
case TIMES :
return x * y;
case DIVIDE :
return x / y;
}
throw new Defect();
}
}

With the polymorphic approach adding a new Operation instance is easy while adding a new method is trickier
The opposite is similarly true.

My own caveat - when in doubt, go for polymorphism.