While it is true that Java erases types at runtime (thus turning List<String> into just List ), in many cases it actually preserves the generic type at runtime, allowing you to recover lost information for deletion. You can get the actual generic types for them:
- method arguments (your case)
- Return methods
- Field types
- Superclasses / Interfaces
This means that if you just have an object of type List, you canβt do anything to get its generic type ( object.getClass() will get you List and that it) - it was completely lost. But, if you are trying to figure out the general type of argument to a method or any of the above, you can usually use reflection. Since your case does not include type variables or other complications, it is quite simple to get the actual type:
ParameterizedType listArgument = (ParameterizedType) ClassDeclaringFoo.class.getMethod("foo", List.class).getGenericParameterTypes()[0]; Type listType = listArgument.getActualTypeArguments()[0];
If instead you had more options and a map:
public String foo(Object bar, Map<String, Number> possibleFoos) { ... }
The code will look like:
ParameterizedType mapArgument = (ParameterizedType) ClassDeclaringFoo.class.getMethod("foo", Object.class, Map.class).getGenericParameterTypes()[1];
We can safely assume that this is what DWR uses, and types are arguments to the method.
Similar methods are available for the other cases listed:
Class.getMethod(...).getGenericReturnType() you will get a real return typeClass.getField(fieldName).getGenericType() will provide you with a real field typeClass.getGenericSuperClass() will give you a real super typeClass.getGenericInterfaces() will provide you with real interface types
There are equivalent methods for accessing AnnotatedType (generic type plus usage type annotations) introduced in Java 8:
Class.getMethod(...).getAnnotatedParameterTypes()Class.getMethod(...).getAnnotatedReturnType()Class.getField(fieldName).getAnnotatedType()Class.getAnnotatedSuperClass()Class.getAnnotatedInterfaces()
Now, it's all dandy when your case is simple, as in the example. But imagine if your example would look like this:
public T foo(List<T> possibleFoos) {...}
In this case, getGenericParameterTypes()[0].getActualTypeArguments()[0] will give you T , which is useless. To decide what T means, you will need to study the definition of the class and possibly the superclasses, keeping track of how the variable names are indicated in each class, since the names can be different in each.
To make it easier to work with type-type descriptions, you can use a wonderful library called GenTyRef that does the hard work for you, and if you need AnnotatedType s support, you can use my fork called GeAnTyRef (both located in Maven Central). They also include a factory type that allows you to create (Annotated)Type instances that you cannot easily make using the regular Java API. It also has a handy supertext mark that allows you to get a literal (Annotated)Type .
With these functions, you can do everything with the generic types that Java allows without the problems I mentioned above:
GenericTypeReflector#getExactParameterTypes( ... )GenericTypeReflector#getExactReturnType( ... )GenericTypeReflector#getExactFieldType( ... )GenericTypeReflector#getExactSuperType( ... )
And many more operations, for example, finding out whether one Type super-type of another (similar to Class#isAssignableFrom , but for general types), allowing certain types of variables, etc.