6

I am having an issue initializing a class with type parameter. It seems to be a shortcoming of Java's type inference and I would like to know if there's a way around this or a better way of achieving this.

public class ParentModel {}

public class ChildModel extends ParentModel {}

public class Service<E extends ParentModel, T extends Collection<E>> {
    private Class<T> classOfT;
    private Class<E> classOfE;

    public Service(Class<E> classOfE, Class<T> classOfT) {
        this.classOfE = classOfE;
        this.classOfT = classOfT;
    }
}

public class BusinessLogic {
    public void someLogic() {
        Service<ChildModel, ArrayList<ChildModel>> service = new 
            Service<ChildModel, ArrayList<ChildModel>>(ChildModel.class, ArrayList.class);
    }
}

Compile-time error is in BusinessLogic::someLogic():

The constructor Service<ChildModel, ArrayList<ChildModel>>(Class<ChildModel>, Class<ArrayList>) is undefined

Compiled to Java 7.

1
  • Ate you intending to have Class fields or fields of whatever type T and E are? Commented Mar 13, 2016 at 18:29

2 Answers 2

2

Because generics in Java are implemented "by erasure", there is no Class<ArrayList<ChildModel>>>, only a Class<ArrayList>.

What you can do is to allow supertypes.

Class<? super T> classOfT;
Class<? super E> classOfE;
public Service(Class<? super E> classOfE, Class<? super T> classOfT) {

alternatively, you can double-cast the class:

Class<ArrayList<Integer>> clazz =
    (Class<ArrayList<Integer>>) (Class<? super ArrayList>) 
    ArrayList.class;

But beware: the class is just ArrayList - Java will not perform additional type checks at runtime on the generics. See for yourself:

ArrayList<Integer> a1 = new ArrayList<>();
ArrayList<Double> a2 = new ArrayList<>();
System.out.println(a1.getClass() == a2.getClass());

It is the same class. At runtime, the generics are virtually gone

Sign up to request clarification or add additional context in comments.

2 Comments

this Class<ArrayList<Something>> clazz = (Class<ArrayList<Something>>) ArrayList.class; should not compile, does it? I like the idea of using super though
Sorry, you need a double-cast, with either super or extends.
1

Since there is no such thing as ArrayList<ChildModel>.class there will be no elegant way to solve this. You can pass a raw type to you your constructor, as mentioned by Yassin, like this:

Service<ChildModel, ArrayList<ChildModel>> s1 = 
        new Service<>(ChildModel.class, (Class) ArrayList.class) 

The difference to your invocation is that here we're using the raw type Class, while in in your example the type Class<ArrayList> is used (so this is no bug).

Another option would be to get the type from an actual instance:

Class<ArrayList<ChildModel>> fromObj = 
        (Class<ArrayList<ChildModel>>) new ArrayList<ChildModel>(0).getClass();

This is more verbose, but I would prefer that over the raw type (in both cases you will get compiler warnings)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.