This thing is troubling me for a while now. I have asked questions before, but probably with a bad phrasing and an example that was too abstract. So it wasn’t clear what I was actually asking. I’ll try again. And please don’t jump to conclusions. I expect that the question is not easy at all to answer!
why can’t I have an enum with generic type parameters in Java?
The question is not about why it’s not possible, syntactically. I know it’s just not supported. The question is: why did the JSR people “forget” or “omit” this very useful feature? I can’t imagine a compiler-related reason, why it wouldn’t be feasible.
Here’s what I would love to do. This is possible in Java. It’s the Java 1.4 way to create typesafe enums:
// A model class for SQL data types and their mapping to Java types
public class DataType<T> implements Serializable, Comparable<DataType<T>> {
private final String name;
private final Class<T> type;
public static final DataType<Integer> INT = new DataType<Integer>("int", Integer.class);
public static final DataType<Integer> INT4 = new DataType<Integer>("int4", Integer.class);
public static final DataType<Integer> INTEGER = new DataType<Integer>("integer", Integer.class);
public static final DataType<Long> BIGINT = new DataType<Long> ("bigint", Long.class);
private DataType(String name, Class<T> type) {
this.name = name;
this.type = type;
}
// Returns T. I find this often very useful!
public T parse(String string) throws Exception {
// [...]
}
// Check this out. Advanced generics:
public T[] parseArray(String string) throws Exception {
// [...]
}
// Even more advanced:
public DataType<T[]> getArrayType() {
// [...]
}
// [ ... more methods ... ]
}
And then, you could use <T> in many other places
public class Utility {
// Generic methods...
public static <T> T doStuff(DataType<T> type) {
// [...]
}
}
But these things are not possible with an enum:
// This can't be done
public enum DataType<T> {
// Neither can this...
INT<Integer>("int", Integer.class),
INT4<Integer>("int4", Integer.class),
// [...]
}
Now, as I said. I know these things have been designed exactly that way. enum is syntactic sugar. So are generics. Actually, the compiler does all the work and transforms enums into subclasses of java.lang.Enum and generics into casts and synthetic methods.
but why can’t the compiler go further and allow for generic enums??
EDIT:
This is what I would expect as compiler-generated Java code:
public class DataType<T> extends Enum<DataType<?>> {
// [...]
}
I’m going to guess a bit and say that it is because of covariance issues on the type parameter of the Enum class itself, which is defined as
Enum<E extends Enum<E>>, although it is a bit much to investigate all the corner cases of that.Besides that, a primary use case of enums is with things like EnumSet and valueOf where you have a collection of things with different generic parameters and get the value from a string, all of which would not support or worse the generic parameter on the enum itself.
I know I’m always in a world of pain when I try to get that fancy with Generics, and I imagine the language designers peeked at that abyss and decided to not go there, especially since the features were developed concurrently, which would mean even more uncertainty for the Enum side of things.
Or put another way, it would have all the problems of
Class<T>in dealing with classes which themselves have generic parameters, and you would have to do a lot of casting and dealing with raw types. Not truly something that the language designers felt was worth it for the type of use case you are looking at.EDIT: In response to the comments (and Tom – a downvote?), nested generic parameter makes all kinds of bad things happen. Enum implements Comparable. That simply would not work to compare two arbitrary elements of the enum in client code if generics were in play. Once you deal with a Generic parameter of a Generic parameter, you end up with all kinds of bounds problems and headaches. It is hard to design a class that handles it well. In the case of comparable, I could not figure out a way to make it work to compare two arbitrary members of an enum without reverting to raw types and getting a compiler warning. Could you?Actually the above is embarrassingly wrong, as I was using the DataType in the question as my template for thinking about this, but in fact an Enum would have a subclass, so that isn’t quite right.
However, I stand by the gist of my answer. Tom brought up
EnumSet.complementOfand of course we still havevalueOfthat produces problems, and to the degree that the design of Enum could have worked, we have to realize that that is a 20/20 hindsight thing. Enum was being designed concurrently with generics and didn’t have the benefit of validating all such corner cases. Especially considering that the use case for an Enum with a generic parameter is rather limited. (But then again, so is the use case for EnumSet).