Google
 
Web unafbapune.blogspot.com

Saturday, April 25, 2009

 

Covariant Enum Idiom

Say we want to have a Java Enum that can return different types from the same method by different constants, how could we go about writing it ? How about something like:
/**
* An enum that can polymorphically return different types of object.
*/
public enum CovariantEnum {
A {
@Override public String getValue() { return "CovariantEnum.A"; }
},
B {
@Override public Integer getValue() { return 1; }
},
// etc.
;
public abstract <T> T getValue();
}
This would actually compile, except there are two problems. First, there are compilation warnings about "return type requires unchecked conversion". Second, it actually doesn't quite work as expected. Client code such as:
// This doesn't work :(
String stringValue = CovariantEnum.A.getValue();
would result in a type mismatch compilation error. Explicit casting the returned value to the target type, such as String in the above example, would work, but that defeats the purpose of building CovariantEnum in the first place.

So how can we get around these limitations ?

Well, first, Java disallows enum declaration to have type parameters. In other words, it is a syntax error to write:
public enum CovariantEnum<T> {
...
But what if we emulate a enum with an abstract class ? This may lead to something like:
/**
* Basically a "CovariantEnum" that actually works and does not cause any compilation warnings.
*/
public abstract class TypeSafeCovariantEnum<T> {
public static final TypeSafeCovariantEnum<String> A = new TypeSafeCovariantEnum<String>() {
@Override public String getValue() { return "TypeSafeCovariantEnum.A"; }
};
public static final TypeSafeCovariantEnum<Integer> B = new TypeSafeCovariantEnum<Integer>() {
@Override public Integer getValue() { return 1; }
};

private TypeSafeCovariantEnum() {}
public abstract T getValue();
}
Now compilation has zero warnings, and a client can enjoy using the "literal instances" in TypeSafeCovariantEnum as if it's a covariant enum without any need for explicit casting:
// This works!
String stringValue = TypeSafeCovariantEnum.A.getValue();
Integer integerValue = TypeSafeCovariantEnum.B.getValue();
Of course this is not an actual enum, and it involves a little complexity inside; but it provides 100% type safety, and simplicity to the clients outside :-)

This page is powered by Blogger. Isn't yours?