我有一个特定的目标类型(在运行时决定),以及一个我可以与之比较的可迭代类。我正在尝试编写一个检查类的泛型参数的方法,以查看它是否是可以迭代我的目标类型的子类。例子:
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>)
答案 0 :(得分:9)
不幸的是,由于类型擦除和反射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
(或子接口)的类,然后向下爬,直到它匹配相应的类型参数。不幸的是,我想不出这是如何实现的。类可以自由地声明具有任何名称的类型参数以及他们想要的任何顺序,因此基于名称或位置的天真匹配是不对的。也许其他人可以提供解决方案。
答案 1 :(得分:1)
我认为你不能只从一个实例中获取泛型参数类型:
new ArrayList<Integer>();
如果您有一个持有该实例的字段,您可以从那里获取它:
private List<Integer> list = ArrayList<Integer>();
看到这个类似的问题: Get generic type of java.util.List