如何使用Java反射检查给定的类是实现Iterable <?对于任何给定的T,扩展T>

Tim*_*rry 5 java generics reflection collections

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

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>)
Run Code Online (Sandbox Code Playgroud)

Pau*_*ora 10

不幸的是,由于类型擦除和反射API的限制相结合,您正在尝试做的事情非常复杂.

这是真的,你可以使用的组合获得了超类的通用参数Class.getGenericSuperclassParameterizedType.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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

让我们在您列出的第一个案例中尝试:

inspect(new ArrayList<SomeObject>());
Run Code Online (Sandbox Code Playgroud)

这打印:

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
Run Code Online (Sandbox Code Playgroud)

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

匿名类的情况不同:

inspect(new Iterable<SomeObject>() {
    @Override
    public Iterator<SomeObject> iterator() {
        throw new UnsupportedOperationException();
    }
});
Run Code Online (Sandbox Code Playgroud)

打印:

class sandbox.Main$1 implements:
  interface java.lang.Iterable with type args: [class sandbox.SomeObject]
class java.lang.Object implements nothing
Run Code Online (Sandbox Code Playgroud)

这里,我们在运行时可以使用类型参数,因为匿名类通过实现解析了type参数Iterable<SomeObject>.ListOfSomeObjects并且它的任何子类都可以用于同样的原因.

好的,只要继承链中的某个类E一路上解析了类型参数,我们可以匹配它吗?不幸的是,至少没有上述方法:

inspect(new ArrayList<SomeObject>() { });
Run Code Online (Sandbox Code Playgroud)

这打印:

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
Run Code Online (Sandbox Code Playgroud)

你可以看到类型参数ArrayList是已知的SomeObject,但它就是停止的地方.类型参数之间没有连接关系.原因是这段代码:

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

getGenericInterfaces获取接口的类型参数信息的唯一方法,但该方法是由声明Class,而不是Type.每当方法有一个ParameterizedType实例,它保存表示其子类的泛型的状态时,它就被强制调用getRawType,它返回Class没有类型参数信息的单例.它是一个catch-22导致只能获得使用具体类型参数实现的接口的类型参数.

我不知道任何反射API方法将类型参数与它们解析的参数匹配.从理论上讲,人们可以编写反映代码,爬上继承链,找到实现Iterable(或子接口)的类,然后向下爬,直到它匹配相应的类型参数.不幸的是,我想不出这是如何实现的.类可以自由地声明具有任何名称的类型参数以及他们想要的任何顺序,因此基于名称或位置的天真匹配是不对的.也许其他人可以提供解决方案.