如何获取正式方法参数类型的参数?

时间:2017-07-09 19:04:10

标签: java reflection guava type-erasure

我有一个类型为Event<Something>的单个参数的方法,需要提取Something或其原始类型。它可能看起来像

 void method1(Event<MyEnum1> event) {}
 <E extends Enum<E>> method2(Event<E> event) {}
 <K, V> method3(Event<Map<K, V>> event) {}

我想要一个函数映射

method1 -> MyEnum.class
method2 -> Enum.class
method3 -> Map.class

对于method1,使用(ParameterizedType) method.getGenericParameterTypes()很容易。对于method2,我可以使用Guava

TypeToken.of(method.getDeclaringClass())
    .method(method)
    .getTypeParameters()[0]
    .getBounds()

获取Type

TypeToken.of(method.getDeclaringClass())
    .method(method)
    .getParameters()
    .get(0)

获取Parameter。我想,我需要使用Parameter来解析Type,但我被困在这里。

我对method3并不在意,尽管知道一般方法会很好。我不关心实际参数的类型(我知道擦除)。

1 个答案:

答案 0 :(得分:3)

您不需要TypeTokenjava.lang.reflect提供您需要的一切。

您需要考虑多个案例。我将假设所提供的任何方法的第一个参数始终是Event类型(或实际上是参数化类型)。

您需要处理Type的5个子类型。在您的示例中,您需要检查type参数是具体类型(MyEnum),类型变量(E是否由Enum限定),还是参数化类型({ {1}})。

此外,您可以使用Map<K,V>来处理类型变量和提取边界。最后,您必须使用WildcardTypeGenericArrayTypeEvent<T[]>)处理通用数组类型。我把它们遗漏了,但它只是为其他类型重新应用相同的规则。

我假设,对于类型变量的情况,bound可能是其他类型的变量,所以我们需要重复,直到找到具体的绑定。

Event<List<String>[]>

使用小型驱动程序

// get raw type argument of first parameter
public static Class<?> getRawTypeArgument(Method method) {
    Parameter parameter = method.getParameters()[0];
    Type type = parameter.getParameterizedType();
    /// assume it's parameterized
    ParameterizedType parameterizedType = (ParameterizedType) type;
    // assume there's one type argument
    Type typeArgument = parameterizedType.getActualTypeArguments()[0];
    if (typeArgument instanceof TypeVariable<?>) {
        TypeVariable<?> typeVariableArgument = (TypeVariable<?>) typeArgument;
        return recursivelyExtractBound(typeVariableArgument);
    } else if (typeArgument instanceof Class<?>) {
        return (Class<?>) typeArgument;
    } else if (typeArgument instanceof ParameterizedType) {
        ParameterizedType parameterizedTypeArgument = (ParameterizedType) typeArgument;
        return (Class<?>) parameterizedTypeArgument.getRawType();
    } 
    throw new AssertionError("Consider wildcard and generic type");
}

private static Class<?> recursivelyExtractBound(TypeVariable<?> typeVariable) {
    // assume first
    Type bound = typeVariable.getBounds()[0];
    if (bound instanceof Class<?>) {
        return (Class<?>) bound;
    } else if (bound instanceof TypeVariable<?>) {
        TypeVariable<?> nested = (TypeVariable<?>) bound;
        return recursivelyExtractBound(nested);
    } else if (bound instanceof ParameterizedType) {
        ParameterizedType parameterizedTypeArgument = (ParameterizedType) bound;
        return (Class<?>) parameterizedTypeArgument.getRawType();
    }
    throw new AssertionError("Are there others?");
}

以上打印出来

public static void main(String[] args) throws Exception {
    Class<?> clazz = Example.class;
    for (Method method : clazz.getDeclaredMethods()) {
        if (!method.getName().startsWith("method")) {
            continue;
        }
        System.out.println("Method '" + method + "' -> " + getRawTypeArgument(method));
    }
}