不幸的是,由于类型擦除和反射 API 的限制相结合,您尝试做的事情非常复杂。
Class.getGenericSuperclass
确实,您可以使用和的组合来获得超类的通用参数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
(或子接口),然后向下爬,直到它与相应的类型参数匹配。不幸的是,我想不出这将如何实施。类可以自由地声明具有任何名称和任何顺序的类型参数,因此基于名称或位置的天真匹配已被淘汰。也许其他人可以提供解决方案。