【问题标题】:Java generics and type erasureJava 泛型和类型擦除
【发布时间】:2010-02-10 02:56:29
【问题描述】:
给定以下代码:
public void example(Object o) {
if(o instanceof List<MyType>)
//do something
}
我知道,鉴于 Java 处理泛型和类型擦除的方式,这是不可能的(以及为什么不可能)。
我的问题是,最好/最干净的方法是什么?还是我唯一能做的就是检查o 是否是List<?>?
【问题讨论】:
标签:
java
generics
type-erasure
【解决方案1】:
您不能检查超过o instanceof List<?>。
您从 Java 泛型中获得的类型安全仅发生在编译时。
像你这样的方法(它接受一个对象,然后试图弄清楚要做什么),不能很好地适应这种设计。
我可以理解为什么有时需要这种动态方法,但考虑用类型参数的版本来补充它:
public void example(Object o) {
if(o instanceof List<?>)
example((List)o); // can only hope that the type is correct here
}
public void example(List<MyType> list){
// do something
}
在可以使用它们的情况下,您将获得泛型的全部好处。在其他情况下,您必须依赖阅读您的 Javadoc 并仅传递正确类型的人。
即使是上面的方法也无法做到的是List<TypeA> 和List<TypeB> 有两个不同的代码路径。如果这对您来说真的有必要,请考虑使用您自己的包装器类型ListOfTypeA、ListOfTypeB。
根据您需要做什么,甚至可能不需要将列表的已擦除类型作为一个整体来查看,而只需处理各个元素的运行时类型:
for (Object o: list){
if (o instanceof TypeA){
}
if (o instanceof TypeB){
}
}
【解决方案2】:
没有检查 List 中所有项的类型,无法确定 Java 中泛型参数的类型,因为它只存在于编译时并在运行时之前被删除。
我的建议是,除非你绝对需要让方法接受一个对象(例如符合接口规范或重写 Object.equals),以接受你想要的正确类型作为方法的参数并重载该方法与运行该方法时可能需要的各种其他类型。
【解决方案3】:
Java 在编译后会擦除泛型的类型信息,因此您无法以这种方式动态检查类型参数。
如果此代码的所有路径都限制类型参数,那么这样做:
// Return true if object is a list of MyType, false if it is not a list, or is
// empty
boolean isListOfMyType(Object o) {
if (o instanceof List) {
List<?> l = (List<?) o;
return (l.size() > 0 && (l.get(0) instanceof MyType)
}
return true;
}
是类型安全的,尽管它仅在列表不为空时才有效。如果不是,您将需要修改上述内容以检查是否所有项目都通过了 instanceof 测试(或者如果您允许列表中包含空值,它们是否为空)。
另一种选择是创建一个扩展 ArrayList<MyType> 的子类并使用
这是给你的instanceof 支票。
最后但同样重要的是,拥有一个实现List<MyType> 的子类将允许您使用Class.getGenericInterfaces() 方法获取类型参数。详情请见this。
要使最后两种方法中的任何一种起作用,您必须确保列表的创建始终实例化其中一种类型。 IE。如果调用者去构造他们自己的ArrayList<MyType> 它将不起作用。
【解决方案5】:
也许通过在运行时检查对象类型,可能是这样的:
if (o.getClass() == List.class) ...
显然,您必须深入挖掘对象的类类型,以查看它是否与 List<> 和列表的元素类型完全匹配。