Reputation: 26503
I have a method objet.
I would like to extract return Type
with generics and convert it to a Class
in order to pass such information into Spring PropertyResolver
.
Type type = myMethod.getGenericReturnType();
Class<?> returnType = /* ??? */;
environment.getProperty(key, returnType);
Upvotes: 3
Views: 6898
Reputation: 2809
In practice return Type
instances must be one of the following: Class
(E.g. String
), GenericArrayType
(E.g String[]
or T[]
or List<T>[]
), TypeVariable
(E.g. T
) or ParametrizedType
(E.g. List<String>
or List<T>
). In addition Type
can also be WildcardType
(E.g. ?
in List<?>
) but these cannot be use directly as return types.
The following code tries to resolve the class given a instance based of its sub interface amongst those 5. Rarely if ever a Type
won't extend any of 5 in which case we simply say that we cannot proceed with an UnsupportedOperationException
. For example you can create your own synthetic Type
extending class but why would you want to ever do that?
public static Class<?> type2Class(Type type) {
if (type instanceof Class) {
return (Class<?>) type;
} else if (type instanceof GenericArrayType) {
// having to create an array instance to get the class is kinda nasty
// but apparently this is a current limitation of java-reflection concerning array classes.
return Array.newInstance(type2Class(((GenericArrayType)type).getGenericComponentType()), 0).getClass(); // E.g. T[] -> T -> Object.class if <T> or Number.class if <T extends Number & Comparable>
} else if (type instanceof ParameterizedType) {
return type2Class(((ParameterizedType) type).getRawType()); // Eg. List<T> would return List.class
} else if (type instanceof TypeVariable) {
Type[] bounds = ((TypeVariable<?>) type).getBounds();
return bounds.length == 0 ? Object.class : type2Class(bounds[0]); // erasure is to the left-most bound.
} else if (type instanceof WildcardType) {
Type[] bounds = ((WildcardType) type).getUpperBounds();
return bounds.length == 0 ? Object.class : type2Class(bounds[0]); // erasure is to the left-most upper bound.
} else {
throw new UnsupportedOperationException("cannot handle type class: " + type.getClass());
}
}
Notice that the code is untested so it might contain compilation errors. Also I'm not sure how the GenericArrayType
would behave with multi-dimensional array types like T[][]
(perhaps it would return Object[]
rather than Object[][]
if <T>
so we need to do additional work here). Please let me know if any corrections are needed.
In the end what we are trying to do here is to calculate the Erasure class given a Type
I wonder whether there is some "standard" code to just do that, perhaps part of Sun/Oracle compiler or code analyzer tools and you just can use their utilities and save yourself the hassle of coding and maintained it ... I didn't find anything thru a quick look.
Upvotes: 5
Reputation: 131486
You can use a workaround to convert java.lang.reflect.Type
returned by Method.getGenericReturnType()
to the Class
of the generic.
String parsing :
final String typeName = method.getGenericReturnType().getTypeName();
Pattern pattern = Pattern.compile("<(.*)>");
final Matcher matcher = pattern.matcher(typeName);
if (matcher.find()) {
String className = matcher.group(1);
Class<?> clazz = Class.forName(className);
}
You could also downcast java.lang.reflect.Type
to the concrete class used at runtime : sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
but I suppose that it may change according to the JVM.
final Type genericReturnType = method.getGenericReturnType();
sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl typeImpl = (ParameterizedTypeImpl) genericReturnType;
String className = typeImpl.getActualTypeArguments()[0].getTypeName();
Class<?> clazz = Class.forName(className);
Upvotes: 1