假设我有一个方法:
public class AwesomeClass {
public <E> List<E> convertIterableToList(Iterable<E> iterable) {
...
}
}
在运行时,如何根据参数类型解析方法的返回类型?例如,我希望实现一个假设的方法resolveReturnType
,它的行为在这个小的(伪java)单元测试中演示:
Method method = AwesomeClass.class.getDeclaredMethod("convertIterableToList", Iterable.class);
Type argumentType = {{{Set<String>}}}; // Just pretend this syntax works. :)
Type expectedReturnType = {{{List<String>}}};
Type actualReturnType = resolveReturnType(method, argumentType);
assertEquals(expectedReturnType, actualReturnType);
到目前为止,我一直在尝试使用Guava的TypeToken类,但我一直没有取得太大进展。
所以,这实际上是可能的,只要您有可用的方法参数的实际正式Type
s。正如@JvR所指出的,这是不可能的一般在运行时,但如果(如在您的示例中)您能够使用TypeToken
或其他类似的显式指定这些类型,它确实工作。
static Type resolveReturnType(Type classType, Method method, Type... argTypes) {
// this should resolve any class-level type variables
Type returnType = TypeToken.of(classType)
.resolveType(method.getGenericReturnType()).getType();
Type[] parameterTypes = method.getGenericParameterTypes();
TypeResolver resolver = new TypeResolver();
for (int i = 0; i < parameterTypes.length; i++) {
@SuppressWarnings("unchecked") // necessary for getSupertype call to compile
TypeToken<Object> paramType =
(TypeToken<Object>) TypeToken.of(parameterTypes[i]);
@SuppressWarnings("unchecked") // necessary for getSupertype call to compile
TypeToken<Object> argType =
(TypeToken<Object>) TypeToken.of(argTypes[i]);
if (method.isVarArgs() && i == parameterTypes.length - 1) {
// TODO
} else {
TypeToken<?> argTypeAsParamType =
argType.getSupertype(paramType.getRawType());
resolver = resolver.where(
paramType.getType(), argTypeAsParamType.getType());
}
}
return resolver.resolveType(returnType);
}
上面的代码有一些漏洞:例如,在给定参数类型为String[]
的情况下,它不能正确解析E foo(E[] array)
的返回类型。当然,对于任何返回类型具有未在其参数类型中使用的类型变量的泛型方法,它也无能为力。我也没有尝试过其他的东西,比如通配符。但是对于您的示例,它工作,它还处理由类声明的类型变量(如果它是一个实例方法)除了由方法声明的类型变量:
public class Foo<T> {
public <E> Map<T, E> convertIterableToMap(Iterable<E> iterable) {
return null;
}
public static void main(String[] args) throws Exception {
Method method = Foo.class.getMethod("convertIterableToMap", Iterable.class);
Type instanceType = new TypeToken<Foo<Integer>>() {}.getType();
Type setOfString = new TypeToken<Set<String>>() {}.getType();
// prints: java.util.Map<java.lang.Integer, java.lang.String>
System.out.println(resolveReturnType(instanceType, method, setOfString));
}
}
简短的回答:你不能。
长答:
<E> List<E> convertIterableToList(Iterable<E> iterable)
有一个类型E
,它通常是不可验证的。您可以检查提供的可迭代对象是否在其类定义中固定了这种类型(1),这意味着您可以检索并找出E
在特定调用中的含义。
但是在一般情况下,运行时不会知道任何特定调用的E
是什么。
(1)
含义类似于class StringList implements List<String>
,其中类型变量是固定的