I would like to request for an item class of collection (kind of specific reflection). But regarding to type erasure it seems not possible and also regarding to some topics I've read here on stack. There are some workarounds (here), but I'm curious if somebody know how is it done for example by DWR:
http://directwebremoting.org/dwr/documentation/server/configuration/dwrxml/signatures.html
or in case that there is some better workaround it would be great.
Let's say we have something like:
public String foo(List<String> possibleFoos) {
and all I need to is find out that parameter possibleFoos is list of Strings, not just List
While it's true that Java will erase types at runtime (thus turning List<String>
into just List
), in many cases it actually does store the generic type in runtime allowing you to restore the information lost to erasure.
You can retrieve the actual generic types for these:
- Method arguments (your case)
- Method return types
- Field types
- Super classes/interfaces
This means if you simply have an object of type List, there's nothing you can do to get it's generic type (object.getClass()
will get you List
and that's it) - it's been permanently lost. But, if you're trying to figure out the generic type for a method argument or any of the above, you usually can by using reflection. As your case doesn't involve type variables or other complications, it's pretty straightforward to get the actual type:
ParameterizedType listArgument = (ParameterizedType) ClassDeclaringFoo.class.getMethod("foo", List.class).getGenericParameterTypes()[0];
Type listType = listArgument.getActualTypeArguments()[0]; //This will be a Class representing String, the type of the List
If you had a more parameters and a map instead:
public String foo(Object bar, Map<String, Number> possibleFoos) { ... }
The code would be similar:
ParameterizedType mapArgument = (ParameterizedType) ClassDeclaringFoo.class.getMethod("foo", Object.class, Map.class).getGenericParameterTypes()[1]; //1 is the index of the map argument
Type keyType = mapArgument.getActualTypeArguments()[0]; //This will be a Class representing String, the type of the Map key
Type valType = mapArgument.getActualTypeArguments()[1]; //This will be a Class representing Number, the type of the Map value
It's safe to assume this is what DWR is using as well, as the types are method arguments.
Similar methods are available for other listed cases:
Class.getMethod(...).getGenericReturnType()
will get you the real return type
Class.getField(fieldName).getGenericType()
will get you the real type of the field
Class.getGenericSuperClass()
will get you the real super type
Class.getGenericInterfaces()
will get you the real interface types
Equivalent methods exist allowing access to AnnotatedType
(generic type plus annotations on the type usage) introduced in Java 8:
Class.getMethod(...).getAnnotatedParameterTypes()
Class.getMethod(...).getAnnotatedReturnType()
Class.getField(fieldName).getAnnotatedType()
Class.getAnnotatedSuperClass()
Class.getAnnotatedInterfaces()
Now, this is all dandy when your case as simple as in the example.
But, imagine if your example looked like this:
public T foo(List<T> possibleFoos) {...}
In this case, getGenericParameterTypes()[0].getActualTypeArguments()[0]
would give you T
which is rather useless. To resolve what T
stands for, you'd have to look into the class definition, and perhaps super classes, while keeping track of how the type variables are named in each class, as the names could be different in each.
To make working with generic type reflection easier, you can use a wonderful library called GenTyRef that does the hard work for you, and if you need support for AnnotatedType
s, you can use my fork called GeAnTyRef (both are in Maven Central). They also include a type factory, that allows you to construct instances of (Annotated)Type
, which you can not easily do using normal Java API. There's also a handy super type token implementation allowing you to get an (Annotated)Type
literal.
With those, you can do everything with generic types that Java allows without the hassle I explained above:
GenericTypeReflector#getExactParameterTypes( ... )
GenericTypeReflector#getExactReturnType( ... )
GenericTypeReflector#getExactFieldType( ... )
GenericTypeReflector#getExactSuperType( ... )
And many more operations, like figuring out if one Type
is a super type of another (similar to Class#isAssignableFrom
but for generic types), resolving specific type variables etc.