tags:

views:

884

answers:

3

Since generics were introduced, Class is parametrized, so that List.class produces Class<List>. This is clear.

What I am not able to figure out is how to get a instance of Class of type which is parametrized itself, i.e. Class<List<String>>. Like in this snippet:

public class GenTest {
    static <T> T instantiate(Class<T> clazz) throws Exception {
        return clazz.newInstance();
    }
    public static void main(String[] args) throws Exception {
        // Is there a way to avoid waring on the line below
        // without using  @SuppressWarnings("unchecked")?
        // ArrayList.class is Class<ArrayList>, but I would like to
        // pass in Class<ArrayList<String>>
        ArrayList<String> l = GenTest.instantiate(ArrayList.class);
    }
}

I run into variations of this problem quite often and I still don't know, if I just miss something, or if there is really no better way. Thanks for suggestions.

+5  A: 

The Class class is a run-time representation of a type. Since parametrized types undergo type erasure at runtime, the class object for Class would be the same as for Class<List<Integer>> and Class<List<String>>.

The reason you cannot instantiate them using the .class notation is that this is a special syntax used for class literals. The Java Language Specification specifically forbids this syntax when the type is parametrized, which is why List<String>.class is not allowed.

Avi
All the logic behind erasure is clear to me. The question is purely about how to associate T with ArrayList<String> to make the method result type match the variable it is assigned to - to make the compiler happy without any warnings.
Michal
Any code working with Class objects will only have access to the types after erasure. So if you have a List<Integer> intList, you will be able to call instantiate(intList.getClass()) and the actual Class object will be List.class.
Avi
+1  A: 

Classes represent classes loaded by a class loader, which are raw types. To represent a parameterized type, use java.lang.reflect.ParameterizedType.

Tom Hawtin - tackline
Can you expand on this, perhaps with an example to help out those who have never used the java.lang.reflect.ParameterizedType before?
PintSizedCat
Anyway, the ParameterizedType interface does not provide any way to creare create a instance...
Nicolas
+1  A: 

I don't think that you can do what you are trying. Firstly, your instantiate method doesn't know that its dealing with a parameterised type (you could just as easily pass it java.util.Date.class). Secondly, because of erasure, doing anything particularly specific with parameterised types at runtime is difficult or impossible.

If you were to approach the problem in a different way, there are other little tricks that you can do, like type inference:

public class GenTest
{
    private static <E> List<E> createList()
    {
        return new ArrayList<E>();
    }

    public static void main(String[] args)
    {
        List<String> list = createList();
        List<Integer> list2 = createList();
    }
}
Dan Dyer