Java Generics and Enum, loss of template parameters

Okay, first you need to understand why what you’re doing is probably not what you think it’s doing. Let’s look at a simpler example.

interface Face {
    <T> List<T> get();
}

What you have there is a generic method, get. A generic method’s type parameter depends on what is supplied by the call site. So for example like this:

Face f = ...;
// this call site dictates T to be Number
List<Number> l = f.<Number>get();

When you override it like

class Impl implements Face {
    @Override
    public List<String> get() { return ...; }
}

This is something you are able to do (only because of erasure) but you probably shouldn’t. It’s only allowed for backwards compatibility to non-generic code. You should listen to the warning and not do it. Doing it means that for example I can still come along and dictate it to return something else:

Face f = new Impl();
// now I've caused heap pollution because you
// actually returned to me a List<String>
List<Number> l = f.<Number>get();

This is why there is an unchecked conversion.

What you probably meant is to use a generic interface declaration:

interface Face<T> {
    List<T> get();
}

Now the argument to T depends on the type of the object reference.

Face<Number> f = ...;
// get must return List<Number>
List<Number> l = f.get();

We can implement it like

class Impl implements Face<String> {
    @Override
    public List<String> get() { return ...; }
}

Additionally, you cannot access covariant return types on an enum. When you override methods on an enum constant, its class is anonymous. An anonymous class has no name and cannot be referred to. Therefore the programmer cannot know its covariant return type to use it. Furthermore, an enum cannot declare generic type parameters. So what you are wanting to do is simply impossible with enum.

You can use a class with public static final instances to simulate a generic enum:

public abstract class SimEnum<T> implements Face<T> {
    public static final SimEnum<Number> A = new SimEnum<Number>() {
        @Override
        public List<Number> get() { return ...; }
    };
    public static final SimEnum<String> B = new SimEnum<String>() {
        @Override
        public List<String> get() { return ...; }
    };

    private SimEnum() {}

    public static SumEnum<?>[] values() {
        return new SimEnum<?>[] { A, B };
    }
}

Otherwise you need to drastically change your idea.

Leave a Comment