我正在使用代码,其中作者在泛型定义中使用原始类型。使用原始类型时,编译器似乎根本不识别泛型类型,即使在原始类中显式定义了泛型类型。
让我用一个例子解释一下。我们有一个豆子:
class Bean<T> {
private List<String> list = new ArrayList<String>();
private T object;
public T getObject() {
return object;
}
public void setObject(T object) {
this.object = object;
}
public List<String> getList() {
return list;
}
}
还有一个包装器(代码正在使用Apache Wicket,我只是将它抽象出来):
class Wrapper<S> {
private S wrapped;
public S getWrapped() {
return wrapped;
}
public void setWrapped(S wrapped){
this.wrapped = wrapped;
}
}
现在我们的人们以半通用的方式使用Wrapper,编译器不会发出警告(不幸的是):
Wrapper<Bean> wrapper = new Wrapper<Bean>();
wrapper.setWrapped(new Bean<Integer>());
// The next line returns a raw type even though the List<String>
// type is stated explicitly in Bean.class and known by the compiler
wrapper.getWrapped().getList();
当然,我们不应该使用原始类型,而是声明Wrapper<Bean<?>>
。不过,我想知道为什么编译器对此如此严格。编译器在解析类型时是否设置某种原始标志并忽略所有通用信息,即使是已知的信息?这就是我的样子。我可以在JLS中找到相应部分的明确答案。我也想知道这个决定背后是否有理由。
答案 0 :(得分:2)
使用原始类型意味着使用该类型的擦除。这意味着将删除所有泛型类型信息(一个有趣的问题是为什么静态字段不是这种情况)。正如您所指的那部分JLS所示,原始类型仅用于传统(1.5之前的)代码(禁止某些特定情况,您没有其他选项)。遗留代码无论如何都无法使用任何通用类型信息,因此保持理论上可以知道的信息毫无意义。
答案 1 :(得分:0)
对于原始类型,我们存在类型不安全操作的风险,并且警告显示原始类型绕过泛型类型检查,将不安全代码的捕获推迟到运行时。继续你的例子:
Wrapper<Bean> wrapper = new Wrapper<>();//<--- raw type warning
Bean<Integer>aBean = new Bean<>();
aBean.setObject(1); //<--- setting object of integer
wrapper.setWrapped(aBean);
Bean<String>beanString = wrapper.getWrapped(); //<--- allowed but with warning
String s = beanString.getObject();//<--- now what ? Should it be allowed ?
使用上面的示例,正如评论所说:错误的类型转换应该生成错误,但是由于原始类型而在编译类型中允许它,而编译器会生成警告,其中术语"unchecked"
表示编译器没有足够的类型信息来执行确保类型安全所必需的所有类型检查。但是,在运行时会产生错误,从而导致不必要的开销。
Raw Types
的官方教程页解释了这一点。