为什么这种(​​误)使用Java泛型不能编译?

时间:2012-01-16 23:20:48

标签: java generics

  

可能重复:
  Why won't this generic java code compile?

给出以下代码:

import java.util.Collections;
import java.util.List;

public class ComeGetSome {
  //TODO: private final Some<?> some = new Some();
  private final Some some = new Some();

  public static void main(String[] args) {
    new ComeGetSome().dude();
  }

  public void dude() {
    for (String str : some.getSomeStrings()) { //FIXME: does not compile!
      System.out.println(str);
    }
  }
}

class Some<T> {
  public List<String> getSomeStrings() {
    return Collections.<String> emptyList();
  }
}

它无法编译,因为some.getSomeStrings()返回原始List。但方法签名指定它返回List<String>

它以某种方式涉及Some具有类型声明但被引用为原始类型的事实。使用对Some<?>的引用可以解决问题。但是该方法与类上的类型声明无关!

为什么编译器的行为如此?

2 个答案:

答案 0 :(得分:4)

如果使用泛型类的原始(无类型)实例,则将其视为完全 raw,即忽略所有泛型类型信息,即使被忽略的类型为 与已省略的泛型类型相关。

这就是为什么......

private final Some<?> some = new Some();

...修复错误 - 它使用类型版本的Some(虽然是通配符)

答案 1 :(得分:1)

首先,如果您实际上没有使用已定义的类型参数,则声明类Some泛型是没有意义的。这实际上是问题的根源。您在此fasion中实例化该类型的成员字段:

private final Some some = new Some();

这是原始类型的声明 - 这是一种泛型类型,其中省略了类型参数。这实际上不仅意味着它自己的类型参数T被抛弃,而且该类的方法中使用的泛型类型的全部也被忽略,包括<String>参数in public List<String> getSomeStrings()

所以解决方案实际上很简单,引用Joshua Bloch:

  

不要在新代码中使用原始类型。

为了使这个具体,你建议修复:

private final Some<?> some = new Some();

修复了问题,但实际上会生成编译器警告(未经检查的操作)。 clean 方式可以在语句的右侧声明实际的类型参数,例如:

private final Some<String> some = new Some<String>();

您在泛型参数中输入的类型可以是您想要的任何类型,它取决于您打算使用Some类的方式。但是如果你实际上不需要type参数,只需删除它,代码就可以正常工作:

class Some { ... }

附注:没有必要在此代码中明确声明String参数:

return Collections.<String> emptyList();

编译器从方法签名中推断出泛型类型,因此您可以将代码简化为:

public List<String> getSomeStrings() {
    return Collections.emptyList();
  }

修改: 谈到Joshua Bloch,在Google I / O 2011的演讲中,有一个关于这个实际问题的Java益智游戏:http://www.youtube.com/watch?v=wbp-3BJWsU8&t=36m04s