[英]How to use Java reflection to check a given class is implements Iterable<? extends T>, for any given T
I have a specific target type (decided at runtime), and an iterable class that I'm comparing to it. 我有一个特定的目标类型(在运行时决定),以及一个可迭代的类,我正在与它进行比较。 I'm trying to write a method that checks the generic parameters of the class, to see if it's an iterable of something that subclasses my target type.
我正在尝试编写一个检查类的泛型参数的方法,以查看它是否是可以迭代我的目标类型的子类。 Examples:
例子:
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>)
Unfortunately, what you're trying to do is very involved, because of the combination of type erasure and limitations of the reflections API. 不幸的是,由于类型擦除和反射API的限制相结合,您正在尝试做的事情非常复杂。
It's true that you can get the generic arguments of a superclass using a combination of Class.getGenericSuperclass
and ParameterizedType.getActualTypeArguments
. 确实,您可以使用
Class.getGenericSuperclass
和ParameterizedType.getActualTypeArguments
的组合来获取超类的泛型ParameterizedType.getActualTypeArguments
。 This is the mechanism that eg Guava's TypeToken
class uses to capture generic type arguments. 这是例如Guava的
TypeToken
类用于捕获泛型类型参数的机制。 But what you're asking for here is the generic type argument of an interface that may have been implemented at any point in the inheritance chain - notwithstanding that interfaces can themselves inherit from each other while freely resolving or declaring new type parameters. 但是你在这里要求的是接口的泛型类型参数,它可以在继承链的任何一点实现 - 尽管接口本身可以在自由解析或声明新类型参数的同时继承。
To demonstrate, take the following method: 要演示,请采用以下方法:
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();
}
}
This will reflect on an object and climb its inheritance chain to report on its implemented interfaces and their generic arguments (if applicable). 这将反映一个对象并爬上它的继承链来报告它实现的接口及其泛型参数(如果适用)。
Let's try it on the first case you listed: 让我们在您列出的第一个案例中尝试:
inspect(new ArrayList<SomeObject>());
This prints: 这打印:
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
You can see that the type parameter E
has not been resolved. 您可以看到类型参数
E
尚未解析。 This is perfectly understandable given type erasure - at runtime the bytecode instructions corresponding to new ArrayList<SomeObject>()
have no concept of SomeObject
. 在给定类型擦除的情况下,这是完全可以理解的 - 在运行时,对应于
new ArrayList<SomeObject>()
的字节码指令没有SomeObject
概念。
The case of the anonymous class is different: 匿名类的情况不同:
inspect(new Iterable<SomeObject>() {
@Override
public Iterator<SomeObject> iterator() {
throw new UnsupportedOperationException();
}
});
Prints: 打印:
class sandbox.Main$1 implements:
interface java.lang.Iterable with type args: [class sandbox.SomeObject]
class java.lang.Object implements nothing
Here, we have the type argument available at runtime since the anonymous class resolved the type parameter by implementing Iterable<SomeObject>
. 这里,我们在运行时可以使用类型参数,因为匿名类通过实现
Iterable<SomeObject>
解析类型参数。 ListOfSomeObjects
and any of its subclasses would work for the same reason. ListOfSomeObjects
及其任何子类都可以用于相同的原因。
Okay, so as long as some class in the inheritance chain resolves the type parameter E
along the way, we can match against it? 好的,只要继承链中的某个类一路上解析了类型参数
E
,我们可以匹配它吗? Unfortunately no, at least not with the method above: 不幸的是,至少没有上述方法:
inspect(new ArrayList<SomeObject>() { });
This prints: 这打印:
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
You can see that the type argument for ArrayList
is known to be SomeObject
, but that's where it stops. 您可以看到
ArrayList
的类型参数已知为SomeObject
,但这就是它停止的位置。 There's no connecting relationship between the type parameters. 类型参数之间没有连接关系。 The reason is this bit of code:
原因是这段代码:
Class<?> rawType =
(type instanceof ParameterizedType)
? (Class<?>)((ParameterizedType)type).getRawType()
: (Class<?>)type;
Type[] interfaceTypes = rawType.getGenericInterfaces();
getGenericInterfaces
is the only way to get type argument information for the interfaces, but that method is declared by Class
, not Type
. getGenericInterfaces
是获取接口的类型参数信息的唯一方法,但该方法由Class
声明,而不是Type
。 Whenever the method has a ParameterizedType
instance, which holds the state representing its subclass's generic-ness, it's forced to call getRawType
, which returns the Class
singleton devoid of type argument information. 每当方法有一个
ParameterizedType
实例,它保存表示其子类的泛型的状态时,就强制调用getRawType
,它返回没有类型参数信息的Class
单例。 It's a catch-22 that results in only being able to get the type arguments of interfaces implemented with concrete type arguments. 它是一个catch-22导致只能获得使用具体类型参数实现的接口的类型参数。
I don't know of any reflection API method that matches type arguments with the parameters they resolve. 我不知道任何反射API方法将类型参数与它们解析的参数匹配。 Theoretically one could write reflective code that climbed up the inheritance chain, located the class that implemented
Iterable
(or a subinterface) and then climbed back down until it matched the corresponding type argument. 从理论上讲,人们可以编写反映代码,爬上继承链,找到实现
Iterable
(或子接口)的类,然后向下爬,直到匹配相应的类型参数。 Unfortunately I can't think if how this would be implemented. 不幸的是,我想不出这是如何实现的。 Classes are free to declare type parameters with any name and in any order they want, so naive matching based on name or position is out.
类可以自由地声明具有任何名称的类型参数以及他们想要的任何顺序,因此基于名称或位置的天真匹配是不对的。 Maybe somebody else can contribute a solution.
也许其他人可以提供解决方案。
I don't think that you can get the generic parameter type from just an instance: 我不认为你可以从一个实例获得泛型参数类型:
new ArrayList<Integer>();
If you had a field holding the instance you can get it from there: 如果你有一个持有实例的字段,你可以从那里得到它:
private List<Integer> list = ArrayList<Integer>();
See this similar question: Get generic type of java.util.List 看到这个类似的问题: 获取java.util.List的泛型类型
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.