Is it possible to get the type of a generic parameter?
An example:
public final class Voodoo {
public static void chill(List> aListWithTy
Here is another trick. Use a generic vararg array
import java.util.ArrayList;
class TypedArrayList<E> extends ArrayList<E>
{
@SafeVarargs
public TypedArrayList (E... typeInfo)
{
// Get generic type at runtime ...
System.out.println (typeInfo.getClass().getComponentType().getTypeName());
}
}
public class GenericTest
{
public static void main (String[] args)
{
// No need to supply the dummy argument
ArrayList<Integer> ar1 = new TypedArrayList<> ();
ArrayList<String> ar2 = new TypedArrayList<> ();
ArrayList<?> ar3 = new TypedArrayList<> ();
}
}
I've coded this for methods which expect to accept or return Iterable<?...>
. Here is the code:
/**
* Assuming the given method returns or takes an Iterable<T>, this determines the type T.
* T may or may not extend WindupVertexFrame.
*/
private static Class typeOfIterable(Method method, boolean setter)
{
Type type;
if (setter) {
Type[] types = method.getGenericParameterTypes();
// The first parameter to the method expected to be Iterable<...> .
if (types.length == 0)
throw new IllegalArgumentException("Given method has 0 params: " + method);
type = types[0];
}
else {
type = method.getGenericReturnType();
}
// Now get the parametrized type of the generic.
if (!(type instanceof ParameterizedType))
throw new IllegalArgumentException("Given method's 1st param type is not parametrized generic: " + method);
ParameterizedType pType = (ParameterizedType) type;
final Type[] actualArgs = pType.getActualTypeArguments();
if (actualArgs.length == 0)
throw new IllegalArgumentException("Given method's 1st param type is not parametrized generic: " + method);
Type t = actualArgs[0];
if (t instanceof Class)
return (Class<?>) t;
if (t instanceof TypeVariable){
TypeVariable tv = (TypeVariable) actualArgs[0];
AnnotatedType[] annotatedBounds = tv.getAnnotatedBounds();///
GenericDeclaration genericDeclaration = tv.getGenericDeclaration();///
return (Class) tv.getAnnotatedBounds()[0].getType();
}
throw new IllegalArgumentException("Unknown kind of type: " + t.getTypeName());
}
Use:
Class<?> typeOfTheList = aListWithTypeSpiderMan.toArray().getClass().getComponentType();
You cannot get a generic parameter from a variable. But you can from a method or field declaration:
Method method = getClass().getDeclaredMethod("chill", List.class);
Type[] params = method.getGenericParameterTypes();
ParameterizedType firstParam = (ParameterizedType) params[0];
Type[] paramsOfFirstGeneric = firstParam.getActualTypeArguments();
Actually I got this to work. Consider the following snippet:
Method m;
Type[] genericParameterTypes = m.getGenericParameterTypes();
for (int i = 0; i < genericParameterTypes.length; i++) {
if( genericParameterTypes[i] instanceof ParameterizedType ) {
Type[] parameters = ((ParameterizedType)genericParameterTypes[i]).getActualTypeArguments();
//parameters[0] contains java.lang.String for method like "method(List<String> value)"
}
}
I'm using jdk 1.6
I noticed that many people lean towards the getGenericSuperclass()
solution:
class RootGeneric<T> {
public Class<T> persistentClass = (Class<T>)
((ParameterizedType)getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
However, this solution is error prone. It will not work properly if there are generics in the descendants. Consider this:
class Foo<S> extends RootGeneric<Integer> {}
class Bar extends Foo<Double> {}
Which type will Bar.persistentClass
have? Class<Integer>
? Nope, it will be Class<Double>
. This will happen due to getClass()
always returns the top most class, which is Bar
in this case, and its generic super class is Foo<Double>
. Hence, the argument type will be Double
.
If you need a reliable solution which doesn't fail I can suggest two.
Guava
. It has a class that was made exactly for this purpose: com.google.common.reflect.TypeToken
. It handles all the corner cases just fine and offers some more nice functionality. The downside is an extra dependency. Given you've used this class, your code would look simple and clear, like this:class RootGeneric<T> {
@SuppressWarnings("unchecked")
public final Class<T> persistentClass = (Class<T>) (new TypeToken<T>(getClass()) {}.getType());
}
abstract class RootGeneric<T> {
@SuppressWarnings("unchecked")
private Class<T> getTypeOfT() {
Class<T> type = null;
Class<?> iter = getClass();
while (iter.getSuperclass() != null) {
Class<?> next = iter.getSuperclass();
if (next != null && next.isAssignableFrom(RootGeneric.class)) {
type =
(Class<T>)
((ParameterizedType) iter.getGenericSuperclass()).getActualTypeArguments()[0];
break;
}
iter = next;
}
if (type == null) {
throw new ClassCastException("Cannot determine type of T");
}
return type;
}
}