Jason
Jason

Reputation: 13986

Java send parameterized generic type as valueType

I have a method that returns a type that it is given:

T foo(Class<T> valueType) {...};

String s = foo(Java.lang.String.class);

I'm trying to send a generic type as my class, but getting compiler errors. For example, let's say I want to return an ArrayList of Strings:

ArrayList<String> list = foot(ArrayList<String>.class)

the parameter "String" is giving errors. Is there anyway I can specify the generic type to return?

Upvotes: 2

Views: 2503

Answers (5)

Saintali
Saintali

Reputation: 4571

Assuming you are here only concerned about static types, this trick might be useful:

@SuppressWarnings("unchecked")
public static <T>
Class<T> appropriateClass(T... args) {
    return (Class<T>) args.getClass().getComponentType();
}

Class<ArrayList<String>> c = appropriateClass();
ArrayList<String> list = foo(c);

Actually I am not very proud of the trick. But you might want to give it a try.

If you test it:

System.out.println(c); // prints "class java.util.ArrayList"

So it is equivalent to an ugly cast like (Class<ArrayList<String>>) (Class<?>) ArrayList.class.

If you actually need to have different tokens for ArrayList<String> and ArrayList at runtime, you should use the TypeToken technique as mentioned by Sean Patrick Floyd.

Upvotes: 0

BRPocock
BRPocock

Reputation: 13914

Type Erasure rears its ugly head. Essentially, Java does compile-time checking on the generic <…> clauses, then erases them from the run-time objects, losing the information forever. Nasty, eh? Since .class metaobjects are referenced at run-time, they suffer from the erasure as well.

ArrayList<String>.class is Erased at compile-time, to be ArrayList.class.

If you want type-safety with the generics, you'll have to pass in two parameters, e.g.

ArrayList<String> foo = foot(ArrayList.class, String.class)

Upvotes: 0

newacct
newacct

Reputation: 122489

Just cast the input or output of the method:

ArrayList<String> list = foot((Class<ArrayList<String>>)(Class<?>)ArrayList.class);

or

ArrayList<String> list = (ArrayList<String>)foot(ArrayList.class);

Basically, the Class class does not work well with generics, because it represents a runtime thing, whereas generics works at compile time. At runtime, there is just one class object for ArrayList in the program. Even if you call it Class<ArrayList<String>> or Class<ArrayList<Integer>> or Class<ArrayList>, it's still the same single object. So what type should it be? There's no simple answer that works for everything.

The Java language decided that the class literal ArrayList.class should just have the type Class<ArrayList>. But that causes problems when you want an expression of type Class<ArrayList<String>>, so you have to bend the types.

Upvotes: 0

Hossein
Hossein

Reputation: 41911

Would this work for you(edited)?

ArrayList list = foot(ArrayList.class)

Upvotes: 0

Alexander Pavlov
Alexander Pavlov

Reputation: 32296

The runtime class field value does not depend on the generic type you apply, and this syntax is illegal. More on this here.

Upvotes: 1

Related Questions