Abstract superclass for type-safe enums.
One feature of the C programming language lacking in Java is enumerations. The
C implementation based on ints was poor and open to abuse. The original Java
recommendation and most of the JDK also uses int constants. It has been recognised
however that a more robust type-safe class-based solution can be designed. This
class follows the basic Java type-safe enumeration pattern.
NOTE: Due to the way in which Java ClassLoaders work, comparing
Enum objects should always be done using equals() , not == .
The equals() method will try == first so in most cases the effect is the same.
Of course, if you actually want (or don't mind) Enums in different class
loaders being non-equal, then you can use == .
Simple Enums
To use this class, it must be subclassed. For example:
public final class ColorEnum extends Enum {
public static final ColorEnum RED = new ColorEnum("Red");
public static final ColorEnum GREEN = new ColorEnum("Green");
public static final ColorEnum BLUE = new ColorEnum("Blue");
private ColorEnum(String color) {
super(color);
}
public static ColorEnum getEnum(String color) {
return (ColorEnum) getEnum(ColorEnum.class, color);
}
public static Map getEnumMap() {
return getEnumMap(ColorEnum.class);
}
public static List getEnumList() {
return getEnumList(ColorEnum.class);
}
public static Iterator iterator() {
return iterator(ColorEnum.class);
}
}
As shown, each enum has a name. This can be accessed using getName .
The getEnum and iterator methods are recommended.
Unfortunately, Java restrictions require these to be coded as shown in each subclass.
An alternative choice is to use the
EnumUtils class.
Subclassed Enums
A hierarchy of Enum classes can be built. In this case, the superclass is
unaffected by the addition of subclasses (as per normal Java). The subclasses
may add additional Enum constants of the type of the superclass. The
query methods on the subclass will return all of the Enum constants from the
superclass and subclass.
public final class ExtraColorEnum extends ColorEnum {
// NOTE: Color enum declared above is final, change that to get this
// example to compile.
public static final ColorEnum YELLOW = new ExtraColorEnum("Yellow");
private ExtraColorEnum(String color) {
super(color);
}
public static ColorEnum getEnum(String color) {
return (ColorEnum) getEnum(ExtraColorEnum.class, color);
}
public static Map getEnumMap() {
return getEnumMap(ExtraColorEnum.class);
}
public static List getEnumList() {
return getEnumList(ExtraColorEnum.class);
}
public static Iterator iterator() {
return iterator(ExtraColorEnum.class);
}
}
This example will return RED, GREEN, BLUE, YELLOW from the List and iterator
methods in that order. The RED, GREEN and BLUE instances will be the same (==)
as those from the superclass ColorEnum. Note that YELLOW is declared as a
ColorEnum and not an ExtraColorEnum.
Functional Enums
The enums can have functionality by defining subclasses and
overriding the getEnumClass() method:
public static final OperationEnum PLUS = new PlusOperation();
private static final class PlusOperation extends OperationEnum {
private PlusOperation() {
super("Plus");
}
public int eval(int a, int b) {
return a + b;
}
}
public static final OperationEnum MINUS = new MinusOperation();
private static final class MinusOperation extends OperationEnum {
private MinusOperation() {
super("Minus");
}
public int eval(int a, int b) {
return a - b;
}
}
private OperationEnum(String color) {
super(color);
}
public final Class getEnumClass() { // NOTE: new method!
return OperationEnum.class;
}
public abstract double eval(double a, double b);
public static OperationEnum getEnum(String name) {
return (OperationEnum) getEnum(OperationEnum.class, name);
}
public static Map getEnumMap() {
return getEnumMap(OperationEnum.class);
}
public static List getEnumList() {
return getEnumList(OperationEnum.class);
}
public static Iterator iterator() {
return iterator(OperationEnum.class);
}
}
The code above will work on JDK 1.2. If JDK1.3 and later is used,
the subclasses may be defined as anonymous.
Nested class Enums
Care must be taken with class loading when defining a static nested class
for enums. The static nested class can be loaded without the surrounding outer
class being loaded. This can result in an empty list/map/iterator being returned.
One solution is to define a static block that references the outer class where
the constants are defined. For example:
public final class Outer {
public static final BWEnum BLACK = new BWEnum("Black");
public static final BWEnum WHITE = new BWEnum("White");
// static nested enum class
public static final class BWEnum extends Enum {
static {
// explicitly reference BWEnum class to force constants to load
Object obj = Outer.BLACK;
}
// ... other methods omitted
}
}
Although the above solves the problem, it is not recommended. The best solution
is to define the constants in the enum class, and hold references in the outer class:
public final class Outer {
public static final BWEnum BLACK = BWEnum.BLACK;
public static final BWEnum WHITE = BWEnum.WHITE;
// static nested enum class
public static final class BWEnum extends Enum {
// only define constants in enum classes - private if desired
private static final BWEnum BLACK = new BWEnum("Black");
private static final BWEnum WHITE = new BWEnum("White");
// ... other methods omitted
}
}
For more details, see the 'Nested' test cases.
Lang Enums and Java 5.0 Enums
Enums were added to Java in Java 5.0. The main differences between Lang's
implementation and the new official JDK implementation are:
- The standard Enum is a not just a superclass, but is a type baked into the
language.
- The standard Enum does not support extension, so the standard methods that
are provided in the Lang enum are not available.
- Lang mandates a String name, whereas the standard Enum uses the class
name as its name. getName() changes to name().
Generally people should use the standard Enum. Migrating from the Lang
enum to the standard Enum is not as easy as it might be due to the lack of
class inheritence in standard Enums. This means that it's not possible
to provide a 'super-enum' which could provide the same utility methods
that the Lang enum does. The following utility class is a Java 5.0
version of our EnumUtils class and provides those utility methods.
import java.util.*;
public class EnumUtils {
public static Enum getEnum(Class enumClass, String token) {
return Enum.valueOf(enumClass, token);
}
public static Map getEnumMap(Class enumClass) {
HashMap map = new HashMap();
Iterator itr = EnumUtils.iterator(enumClass);
while(itr.hasNext()) {
Enum enm = (Enum) itr.next();
map.put( enm.name(), enm );
}
return map;
}
public static List getEnumList(Class enumClass) {
return new ArrayList( EnumSet.allOf(enumClass) );
}
public static Iterator iterator(Class enumClass) {
return EnumUtils.getEnumList(enumClass).iterator();
}
}
author: Apache Avalon project author: Stephen Colebourne author: Chris Webb author: Mike Bowler author: Matthias Eichel since: 2.1 (class existed in enum package from v1.0) version: $Id: Enum.java 466285 2006-10-20 22:36:21Z bayard $ |