Get type of a generic parameter in Java with reflection
Is it possible to get the type of a generic parameter?
An example:
public final class Voodoo {
public static void chill(List<?> aListWithTypeSpiderMan) {
// Here I'd like to get the Class-Object 'SpiderMan'
Class typeOfTheList = ???;
}
public static void main(String... args) {
chill(new ArrayList<SpiderMan>());
}
}
Solution 1:
One construct, I once stumbled upon looked like
Class<T> persistentClass = (Class<T>)
((ParameterizedType)getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
So there seems to be some reflection-magic around that I unfortunetly don't fully understand... Sorry.
Solution 2:
I want to try to break down the answer from @DerMike to explain:
First, type erasure does not mean that the JDK eliminates type information at runtime. It's a method for allowing compile-time type checking and runtime type compatibility to coexist in the same language. As this block of code implies, the JDK retains the erased type information--it's just not associated with checked casts and stuff.
Second, this provides generic type information to a generic class exactly one level up the heirarchy from the concrete type being checked--i.e. an abstract parent class with generic type parameters can find the concrete types corresponding to its type parameters for a concrete implementation of itself that directly inherits from it. If this class were non-abstract and instantiated, or the concrete implementation were two levels down, this wouldn't work (although a little bit of jimmying could make it apply to any predetermined number of levels beyond one, or up to the lowest class with X generic type parameters, et cetera).
Anyway, on to the explanation. Here's the code again, separated into lines for ease of reference:
1# Class genericParameter0OfThisClass = 2# (Class) 3# ((ParameterizedType) 4# getClass() 5# .getGenericSuperclass()) 6# .getActualTypeArguments()[0];
Let 'us' be the abstract class with generic types that contains this code. Reading this roughly inside out:
- Line 4 gets the current concrete class' Class instance. This identifies our immediate descendant's concrete type.
- Line 5 gets that class' supertype as a Type; this is us. Since we're a parametric type we can safely cast ourselves to ParameterizedType (line 3). The key is that when Java determines this Type object, it uses type information present in the child to associate type information with our type parameters in the new ParameterizedType instance. So now we can access concrete types for our generics.
- Line 6 gets the array of types mapped into our generics, in order as declared in the class code. For this example we pull out the first parameter. This comes back as a Type.
- Line 2 casts the final Type returned to a Class. This is safe because we know what types our generic type parameters are able to take and can confirm that they will all be classes (I'm not sure how in Java one would go about getting a generic parameter that doesn't have a Class instance associated with it, actually).
...and that's pretty much it. So we push type info from our own concrete implementation back into ourselves, and use it to access a class handle. we could double up getGenericSuperclass() and go two levels, or eliminate getGenericSuperclass() and get values for ourselves as a concrete type (caveat: I haven't tested these scenarios, they haven't come up for me yet).
It gets tricky if your concrete children are be an arbitrary number of hops away, or if you're concrete and not final, and especially tricky if you expect any of your (variably deep) children to have their own generics. But you can usually design around those considerations, so this gets you most of the way.
Hope this helped someone! I recognize this post is ancient. I'll probably snip this explanation and keep it for other questions.
Solution 3:
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