如何使用Java反射来检查给定的类是实现Iterable <? extends T&

我有一个特定的目标类型(在运行时决定)和一个我正在比较的迭代类。 我正在尝试编写一个方法来检查类的泛型参数,以查看它是否可以迭代我的目标类型的子类。 例子:

Class<?> X = SomeObject.class;

matches(X, new ArrayList<SomeObject>()) -> true
matches(X, new ArrayList<SubclassOfSomeObject>()) -> true
matches(X, new ArrayList<SomeOtherObject>()) -> false
matches(X, new ArrayList()) -> true (I think?)
matches(X, new Iterable<SomeObject>() { ... }) -> true
matches(X, new ListOfSomeObjects()) -> true 
                       (where ListOfSomeObjects extends Iterable<SomeObject>)

不幸的是,由于类型擦除和反射API限制的组合,你所要做的事情非常复杂。

确实,您可以使用Class.getGenericSuperclassParameterizedType.getActualTypeArguments的组合来获取超类的泛型ParameterizedType.getActualTypeArguments 。 这是Guava的TypeToken类用来捕获泛型类型参数的机制。 但是你在这里要求的是一个接口的泛型类型参数,它可能在继承链中的任何点都被实现 - 尽管接口本身可以自己继承,而自由地解析或声明新的类型参数。

为了演示,采取以下方法:

static void inspect(Object o) {
    Type type = o.getClass();
    while (type != null) {
        System.out.print(type + " implements");
        Class<?> rawType =
                (type instanceof ParameterizedType)
                ? (Class<?>)((ParameterizedType)type).getRawType()
                : (Class<?>)type;
        Type[] interfaceTypes = rawType.getGenericInterfaces();
        if (interfaceTypes.length > 0) {
            System.out.println(":");
            for (Type interfaceType : interfaceTypes) {
                if (interfaceType instanceof ParameterizedType) {
                    ParameterizedType parameterizedType = (ParameterizedType)interfaceType;
                    System.out.print("  " + parameterizedType.getRawType() + " with type args: ");
                    Type[] actualTypeArgs = parameterizedType.getActualTypeArguments();
                    System.out.println(Arrays.toString(actualTypeArgs));
                }
                else {
                    System.out.println("  " + interfaceType);
                }
            }
        }
        else {
            System.out.println(" nothing");
        }
        type = rawType.getGenericSuperclass();
    }
}

这将反映一个对象并爬上它的继承链来报告其实现的接口和它们的泛型参数(如果适用)。

让我们试试你列出的第一个案例:

inspect(new ArrayList<SomeObject>());

这打印:

class java.util.ArrayList implements:
  interface java.util.List with type args: [E]
  interface java.util.RandomAccess
  interface java.lang.Cloneable
  interface java.io.Serializable
java.util.AbstractList<E> implements:
  interface java.util.List with type args: [E]
java.util.AbstractCollection<E> implements:
  interface java.util.Collection with type args: [E]
class java.lang.Object implements nothing

您可以看到类型参数E尚未解析。 对于类型擦除,这是完全可以理解的 - 在运行时,对应于new ArrayList<SomeObject>()的字节码指令没有SomeObject概念。

匿名类的情况是不同的:

inspect(new Iterable<SomeObject>() {
    @Override
    public Iterator<SomeObject> iterator() {
        throw new UnsupportedOperationException();
    }
});

打印:

class sandbox.Main$1 implements:
  interface java.lang.Iterable with type args: [class sandbox.SomeObject]
class java.lang.Object implements nothing

在这里,我们有运行时可用的类型参数,因为匿名类通过实现Iterable<SomeObject>解析类型参数。 ListOfSomeObjects及其任何子类都会出于同样的原因。

好吧,只要继承链中的某个类沿途解析了类型参数E ,我们可以匹配它吗? 不幸的是,至少不是用上面的方法:

inspect(new ArrayList<SomeObject>() { });

这打印:

class sandbox.Main$1 implements nothing
java.util.ArrayList<sandbox.SomeObject> implements:
  interface java.util.List with type args: [E]
  interface java.util.RandomAccess
  interface java.lang.Cloneable
  interface java.io.Serializable
java.util.AbstractList<E> implements:
  interface java.util.List with type args: [E]
java.util.AbstractCollection<E> implements:
  interface java.util.Collection with type args: [E]
class java.lang.Object implements nothing

你可以看到ArrayList的类型参数被称为SomeObject ,但是它就是停止的地方。 类型参数之间没有连接关系。 原因是这一点的代码:

Class<?> rawType =
        (type instanceof ParameterizedType)
        ? (Class<?>)((ParameterizedType)type).getRawType()
        : (Class<?>)type;
Type[] interfaceTypes = rawType.getGenericInterfaces();

getGenericInterfaces是获取接口的类型参数信息的唯一方式,但该方法由Class声明,而不是Type声明。 无论何时该方法有一个ParameterizedType实例,该实例持有表示其子类的通用性的状态,它就被迫调用getRawType ,它返回没有类型参数信息的Class单例。 这是一个catch-22,它只能得到用具体类型参数实现的接口的类型参数。

我不知道任何反射API方法与它们解析的参数匹配类型参数。 理论上,人们可以编写反射代码,爬上继承链,找到实现Iterable (或子接口)的类,然后向下爬,直到它匹配相应的类型参数。 不幸的是,我不能认为这将如何实施。 类可以自由地声明具有任何名称的类型参数,并且可以按照他们想要的任何顺序进行声明,所以基于名称或位置的朴素匹配就不存在了。 也许别人可以提供解决方案。


我不认为你可以从一个实例中获得泛型参数类型:

new ArrayList<Integer>();

如果你有一个领域持有的实例,你可以从那里得到它:

private List<Integer> list = ArrayList<Integer>();

看到类似的问题:获取java.util.List的泛型类型

链接地址: http://www.djcxy.com/p/76501.html

上一篇: How to use Java reflection to check a given class is implements Iterable<? extends T>, for any given T

下一篇: Animating variable width svg path