views:

60

answers:

2

Suppose I have the following method, which can be used to create a collection of a given type specified.

private static Collection<?> void create(Class<? extends Collection<?>> cls) {
    return cls.newInstance();
}

This is all good if the cls argument is passed in during runtime:

List<String> list = new LinkedList<String>();
create(list.getClass());

But how do I invoke this method in code without an unchecked warning? Say I want to do something like:

create(LinkedList.class);

It'll complain that create(Class) is not defined, which strictly speaking is correct because List is not #capture of Collection, but how do I make it work?

Many thanks!

+3  A: 

Neal Gafter talks about pretty much exactly this problem here. The fix is to use super type tokens. This is what Guice uses to maintain generic type information, I believe.

Basically it's an extra class you can use to represent a type instead of using Foo.class. You'd use it like this:

TypeReference<LinkedList<String>> x = new TypeReference<LinkedList<String>>() {};
create(x);

Note that your attempt to use the raw type LinkedList would still cause the compiler to complain, and deliberately so - when you use raw types, you're basically opting out of type safety. But this scheme allows you to express the generic type in a way which is otherwise harder.

Jon Skeet
A: 

we have two generic types here, one for subtype of collection, one for element type of collection. following code can work:

private static <C extends Collection<E>, E>
C create(Class<C> cls) throws Exception
{
    return cls.newInstance();
}

usage example. no warnings.

    Class<LinkedList<String>> clazz = ...;
    LinkedList<String> result = create(clazz);

the question now becomes how do we obtain clazz? You have to convince the compiler really hard:

    List<String> list = new LinkedList<String>();
    clazz = (Class<LinkedList<String>>)list.getClass();

    clazz = (Class<LinkedList<String>>)(Class<?>)LinkedList.class;

we can have a utility method to ease the type casting:

public static <C extends Collection, E, T extends Collection<E>>
Class<T> type(Class<C> classC, Class<E> classE)
{
    return (Class<T>)classC;
}

//usage:
    clazz = type(LinkedList.class, String.class);
    result = create(clazz);

The fun is recurive:

    Class<LinkedList<LinkedList<String>>> clazz2;
    LinkedList<LinkedList<String>> result2;

    clazz2 = type(LinkedList.class, clazz);
    result2 = create(clazz2);

But at some point, we have to stop this silly game. Generic typing is supposed to help us, not to torture us. When it becomes too cumbersome and makes our programs impossible to read, just drop it. Use the god damn raw types that we have lived with happily for years.

irreputable