【问题标题】:Use of raw types when other generic information is known当其他通用信息已知时使用原始类型
【发布时间】:2013-12-11 09:05:27
【问题描述】:

我正在处理作者在泛型定义中使用原始类型的代码。当使用原始类型时,编译器似乎根本无法识别泛型类型,即使泛型类型是在原始类中显式定义的。

让我用一个例子来解释一下。我们有一个豆子:

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&lt;Bean&lt;?&gt;&gt;。不过,我想知道为什么编译器对此如此严格。编译器在解析类型时是否设置了某种 raw 标志并忽略 all 通用信息,即使是已知的信息?这就是我的样子。阅读JLS 中的相应部分,我可以找到明确的答案。我也想知道这个决定背后是否有原因。

【问题讨论】:

  • 不需要整个Wrapper 部分来证明这一点:仅访问(new Bean()).getList() 也会返回原始List
  • 没错,我从一个实际的应用程序中简化了一些代码,但我做得还不够简单。

标签: java generics javac raw-types


【解决方案1】:

使用原始类型意味着使用该类型的擦除。这意味着所有通用类型信息都被删除(一个有趣的问题是为什么静态字段不是这种情况)。正如您所指的 JLS 的那部分所表明的那样,原始类型只能在遗留(1.5 之前)代码中使用(除非您没有其他选择的某些特定情况)。无论如何,遗留代码将无法使用任何通用类型信息,因此保留理论上可以知道的信息是没有意义的。

【讨论】:

  • 我猜,它不会影响静态类的原始类型,因为类定义永远不是通用的,而只有类的实例可以。由于非泛型实例永远不是原始类型,我猜类本身也永远不会被视为原始类型。我仍然觉得编译器放弃一些类型安全信息很奇怪,但我猜原始类型的发现只是将编译器设置为某种兼容模式。
  • 是的,它执行类型擦除,因此它只保留在运行时也已知的信息。
【解决方案2】:

对于原始类型,我们面临类型不安全操作的风险,警告显示原始类型绕过泛型类型检查,将不安全代码的捕获推迟到运行时。继续你的例子:

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的官方教程页面对此进行了说明。

【讨论】:

  • OP 不是询问Bean.getObject(),而是询问Bean.getList(),它与传递给Bean 的类型参数无关,因此理论上可以保留其通用返回类型List&lt;String&gt;。跨度>
  • 我知道这一点。我正在使用类型不安全的操作示例展示编译器生成此警告消息的原因。编译器不知道未来的操作,但它只是警告如果我们使用原始类型并执行此类操作,它将来可能会遇到麻烦
猜你喜欢
  • 1970-01-01
  • 2010-12-16
  • 1970-01-01
  • 1970-01-01
  • 2010-12-29
  • 2011-08-31
  • 1970-01-01
  • 2011-02-13
  • 1970-01-01
相关资源
最近更新 更多