如何使用Javareflection检查给定的类是实现Iterable

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

Class X = SomeObject.class; matches(X, new ArrayList()) -> true matches(X, new ArrayList()) -> true matches(X, new ArrayList()) -> false matches(X, new ArrayList()) -> true (I think?) matches(X, new Iterable() { ... }) -> true matches(X, new ListOfSomeObjects()) -> true (where ListOfSomeObjects extends Iterable) 

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

确实,您可以使用Class.getGenericSuperclassParameterizedType.getActualTypeArguments的组合来获取超类的genericsParameterizedType.getActualTypeArguments 。 这是例如Guava的TypeToken类用于捕获generics类型参数的机制。 但是你在这里要求的是接口的generics类型参数,它可以在inheritance链的任何一点实现 – 尽管接口本身可以在自由解析或声明新类型参数的同时inheritance。

要演示,请采用以下方法:

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

这将反映一个对象并爬上它的inheritance链来报告它实现的接口及其generics参数(如果适用)。

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

 inspect(new ArrayList()); 

这打印:

 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 implements: interface java.util.List with type args: [E] java.util.AbstractCollection implements: interface java.util.Collection with type args: [E] class java.lang.Object implements nothing 

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

匿名类的情况不同:

 inspect(new Iterable() { @Override public Iterator 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解析类型参数。 ListOfSomeObjects及其任何子类都可以用于相同的原因。

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

 inspect(new ArrayList() { }); 

这打印:

 class sandbox.Main$1 implements nothing 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 implements: interface java.util.List with type args: [E] java.util.AbstractCollection 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实例,该实例保存表示其子类的generics的状态,就会强制调用getRawType ,它返回没有类型参数信息的Class单例。 这是一个catch-22导致只能获得使用具体类型参数实现的接口的类型参数。

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

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

 new ArrayList(); 

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

 private List list = ArrayList(); 

看到这个类似的问题: 获取java.util.List的generics类型