Shortcomings of Java Enums

When Java was first introduced, it had a huge missing piece: enumerated types. Apparently Sun didn't appreciate the lowered project risk brought about by strongly-typed parameters—or care if the language made it just as easy to pass the direction integer argument SOUTH when the currency integer argument DOLLARS was expected. (I'm almost surprised Java didn't originally require FALSE=0 and TRUE=1 integers intead of an actual boolean type!) Now that Java has finally got around to add enums, there's plenty to like (ordinals, singletonality, use in case statements, to name a few things), but they still have some significant and needless shortcomings.

No Custom Names

The way Java implements enums, the programming name of the enum is also the value returned by getName(), as wel as the default serialization returned by toString(). You can then turn around and use MyEnum.valueOf(String) to derive the original enum from a serialized form—but only if the string contains the getName() form. Java conventions are that enum names are all uppercase, which immediately causes problems if you want to serialize the enum in another form.

Let's say that you want to create an enum of sides of the box model in CSS. You could do the following:

enum Side{LEFT, TOP, RIGHT, BOTTOM}

But when you serialize those enum values, you want to use lowercase values of "left", "top", "right", and "bottom". No problem—you can override Side.toString() to return a lowercase version of the enum name. Yet there's no way to override Side.valueOf(String), so you can't turn the serialized string back into a Side because Side.valueOf(String) expects the Side.getName() form, not the Side.toString(). (This problem naturally also occurs if you are using reflection via Enum.valueOf(Class, String).)

The obvious workaround is to use lowercase names to begin with, but this makes your variable names unconventional and inconsistent with your other code; and causes warnings in IDEs when you try to refactor. Java should have allowed the variable name and the serialized form of enums to be distinct. Enums could allow a string constructor that specifies the form returned by getName() and used by MyEnum.valueOf(String), and enum subclasses could call this constructor as follows:

enum Side
{
LEFT("left"),
TOP("top"),
RIGHT("right"),
BOTTOM("bottom");

private Side(String name)
{
super(name);
}
}

Another workaround is to create functions that convert enum-style names into more serialization-friendly names, like this:

public static <E extends Enum<E>> String getSerializationName(E e)
{
return e.name().toLowerCase().replace('_', '-');
}
public static <E extends Enum<E>> E getSerializedEnum(Class<E> enumType, String serializationName)
{
return Enum.valueOf(enumType, serializationName.replace('-', '_').toUpperCase());
}

This way you could automatically convert to and from a serialized form for certain types. But this brings up another serious issue for enums: there is no way to specify that any particular type is also an enum, because there is no enum interface.

No Enum Interface

The Java Enum class is not an interface, and therefore allows no way to specify an arbitrary class or interface as being an enum. Here's an example. Let's say that we have have a method that serializes XML elements from various vocabularies. Here's the method signature:

interface XMLElement{} //tags an instance as an XML element
void serializeElement(XMLElement element); //serializes some XML element

Here's part of the XHTML enumeration, for example:

enum XHTMLElement{P, STRONG, EM}

So we'd like to call serializeElement(XHTMLElement.STRONG), and the method would serialize the element. Inside the method we could use getSerializationName(element), but XMLElement is an interface, not an enum! We need to be able to specify:

interface XMLElement extends Enum{}

But we can't do this because Enum isn't an interface. We could try tricks with generics to ensure that whatever is passed as an XMLElement is also an enum:

void <E extends Enum<E> & XMLElement> serializeElement(E element);

But this requires that we impose this requirement manually for each such method. Moreover, there's no way to declare a variable that is both an XMLElement and an Enum in a general context. The following won't work, for example:

<E extends Enum<E> & XMLElement> E element=XHTMLElement.STRONG;

The lack of custom enum names and the inability to specify a type as an enum seriously limits the usability of enums as anything more than a base use case. This is disappointing because these are needless limitations, but it is a common trend for Java features: Java generics are even more anemic and hardly usable for numerous use cases, but that's an essay in itself.