如何在运行时检查接口泛型类型

时间:2018-01-10 18:02:48

标签: java generics reflection

我试图在运行时获取接口使用的泛型类型Class<?>

所以,如果我有以下

public class A<T> implements B<Z> {}

public interface B<T> extends C<Z> {}

public interface C<T> {}

public class Z {}

A temp = new A<MyClass>();

我希望能够在给定实例Class<?>的情况下获得B,C或D的第一个(或更晚的)通用的temp

现在我有这个,但它只适用于此实例的类直接实现的接口。无论此实例如何继承目标接口,我都想将其修改为工作。

/**
 * Finds this instances implementation of the interfaceClass, and returns
 * the associated generic type
 *
 * @param instance
 *            the instance to extract from
 * @param interfaceClass
 *            The class this instance implements
 * @param paramIndex
 *            Index of the generic class to get the class of
 * @return The class of the generic type
 * @throws ClassNotFoundException
 */
public static <T> Class<?> ParameterizedClass(final T instance, final Class<T> interfaceClass, final int paramIndex)
        throws ClassNotFoundException {
    final Type[] sooper = instance.getClass().getGenericInterfaces();
    for (final Type t : sooper) {
        if (!(t instanceof ParameterizedType)) {
            continue;
        }

        final ParameterizedType type = ((ParameterizedType) t);
        if (type.getRawType().getTypeName().equals(interfaceClass.getTypeName())) {
            return Class.forName(type.getActualTypeArguments()[paramIndex].getTypeName());
        }
    }

    return null;
}

使用我现在的代码和示例类,我得到以下结果

ParameterizedClass(new A<Integer>, B.class, 0); // returns Class<Z>

ParameterizedClass(new A<Integer>, C.class, 0); // returns null, but I want Class<Z>

这就是我直接实现的意思。我无法找到如何阅读继承的类。

3 个答案:

答案 0 :(得分:0)

为了修复你的第二个调用以便它返回Class<Z>我想你需要查询中间接口继承,在这种情况下B是要获得Class<Z>

你可以在第二个循环中使用这样的递归来做到这一点:

public static Class<?> parameterizedClass(final Class<?> root, final Class<?> target, final int paramIndex)
        throws ClassNotFoundException {

    final Type[] sooper = root.getGenericInterfaces();
    for (final Type t : sooper) {
        if (!(t instanceof ParameterizedType)) {
            continue;
        }
        final ParameterizedType type = ((ParameterizedType) t);
        if (type.getRawType().getTypeName().equals(target.getTypeName())) {
            return Class.forName(type.getActualTypeArguments()[paramIndex].getTypeName());
        }
    }
    for (final Class<?> parent : root.getInterfaces()) {
        final Class<?> result = parameterizedClass(parent, target, paramIndex);
        if (result != null) {
            return result;
        }
    }
    return null;
}

public static Class<?> parameterizedClass(final Object object, final Class<?> target, final int paramIndex)
        throws ClassNotFoundException {
    return parameterizedClass(object.getClass(), target, paramIndex);
}

答案 1 :(得分:0)

您可以使用我的实用工具类GenericUtil,它具有以下方法

public static Type[] getGenericTypes(Type sourceType, Class<?> targetClass)

对于你的情况:

System.out.println(Arrays.toString(GenericUtil.getGenericTypes(A.class, B.class)));
System.out.println(Arrays.toString(GenericUtil.getGenericTypes(A.class, C.class)));
System.out.println(Arrays.toString(GenericUtil.getGenericTypes(B.class, C.class)));
System.out.println(Arrays.toString(GenericUtil.getGenericTypes(A.class, Z.class)));

将输出

[class xdean.stackoverflow.java.reflection.Q48193539$Z]
[class xdean.stackoverflow.java.reflection.Q48193539$Z]
[class xdean.stackoverflow.java.reflection.Q48193539$Z]
[]

您可以找到它的实施on github。它有详细的javadoc,它可以解决最复杂的情​​况。例如:

interface I1<A> {
}

interface I2<A, B> {
}

class C5<A> implements I1<I2<A, Object>> {
}

//return Type[]{I2<A, Object> (ParameterziedType)}
GenericUtil.getGenericTypes(C5.class, I1.class);

您可以在its test中找到更复杂的情况。

答案 2 :(得分:-1)

在理论上的java中,由于&#34; erasure&#34;这在运行时是不可能的。我经历过你可以做到但实际上只是为了第一级而且只有sun提供的类,所以不是在android中。在第二级,您只能找到您的标签(或其他)标签。