使用其参数类型解析方法的返回类型

时间:2014-05-19 11:15:08

标签: java generics reflection guava

假设我有一个方法:

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课程,但我一直没有取得多大进展。

2 个答案:

答案 0 :(得分:2)

所以,这实际上是可行的,只要你有可用方法的参数的实际正式Type。正如@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);
}

上面的代码中有一些漏洞:例如,如果参数类型为E foo(E[] array),它将无法正确解析String[]的返回类型。当然,它也无法帮助任何返回类型具有未在其参数类型中使用的类型变量的泛型方法。我还没有尝试过各种其他东西,比如通配符。但是对于你的例子它是有效的,除了方法声明的那些之外,它还处理由类声明的类型变量(如果它是一个实例方法):

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));
  }
}

答案 1 :(得分:1)

简短回答:你不能。

更长的答案:

<E> List<E> convertIterableToList(Iterable<E> iterable)的类型E通常无法恢复。您可以检查提供的iterable是否在其类定义中修复了此类型(1),这意味着您可以检索它并找出E中的含义{特定的调用。

但是在一般情况下,运行时将不知道任何特定调用的E是什么。

(1) 意思是class StringList implements List<String>之类的东西,其中类型变量是固定的。