Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java enums and generics

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<?>> {
    // [...]
}
like image 607
Lukas Eder Avatar asked Feb 24 '11 18:02

Lukas Eder


People also ask

Can enum be used for generics Java?

Java enums will be enhanced with generics support and with the ability to add methods to individual items, a new JEP shows. Since both features can be delivered with the same code change, they are bundled together in the same JEP. The change only affects the Java compiler, and therefore no runtime changes are needed.

What are generics in Java?

Generics means parameterized types. The idea is to allow type (Integer, String, … etc., and user-defined types) to be a parameter to methods, classes, and interfaces. Using Generics, it is possible to create classes that work with different data types.

What is Java enum?

An enum type is a special data type that enables for a variable to be a set of predefined constants. The variable must be equal to one of the values that have been predefined for it. Common examples include compass directions (values of NORTH, SOUTH, EAST, and WEST) and the days of the week.

Can enums be inherited?

Enums cannot inherit from other enums. In fact all enums must actually inherit from System. Enum . C# allows syntax to change the underlying representation of the enum values which looks like inheritance, but in actuality they still inherit from System.


1 Answers

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.complementOf and of course we still have valueOf that 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).

like image 107
Yishai Avatar answered Nov 15 '22 19:11

Yishai