为什么Java编译器抱怨使用带有原始类型的foreach?

Mic*_*wan 7 java generics foreach raw-types

在Java中的for-each循环中使用泛型时,我遇到了一个奇怪的编译器错误.这是一个Java编译器错误,还是我真的在这里遗漏了什么?

这是我的全班:

public class Generics<T extends Object> {
  public Generics(T myObject){
    // I didn't really need myObject
  }

  public List<String> getList(){
    List<String> list = new ArrayList<String>();
    list.add("w00t StackOverflow");
    return list;
  }

  public static void main(String...a){
    Generics generics = new Generics(new Object());
    for(String s : generics.getList()){
      System.out.println(s);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

编译器抱怨带有for-each的行:"类型不匹配无法从元素类型Object转换为String."
如果我进行这种微妙的改变,它会编译:

public static void main(String...a){
  Generics<?> generics = new Generics(new Object());
  for(String s : generics.getList()){
    System.out.println(s);
  }
}
Run Code Online (Sandbox Code Playgroud)

我知道getList()确实使用泛型,但它在我认为完全不相关的方式中使用它们.我可以理解这一点,如果我试图迭代T类型的东西并getList()返回一个List<T>或者什么东西,但事实并非如此.返回类型getList()应该与T完全无关,不应该关心我是否使用我的Generics对象的原始类型......对吗?难道这些不完全无关,还是我真的在这里遗漏了什么?

请注意,如果我这样做,代码也会编译,我认为它应该与第一个相同:

public static void main(String...a){
  Generics generics = new Generics(new Object());
  List<String> list = generics.getList();
  for(String s : list){
    System.out.println(s);
  }
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 12

不同之处在于,当您使用原始类型时,成员签名中的所有通用引用也会转换为其原始表单.所以你有效地调用一个现在有这样签名的方法:

List getList()
Run Code Online (Sandbox Code Playgroud)

现在,为什么你的最终版本会编译 - 尽管如此,如果你使用的话会有一个警告-Xlint:

Generics.java:16: warning: [unchecked] unchecked conversion
    List<String> list = generics.getList();
                                        ^
Run Code Online (Sandbox Code Playgroud)

这类似于:

 List list = new ArrayList();
 List<String> strings = list;
Run Code Online (Sandbox Code Playgroud)

......也会编译,但会发出警告-Xlint.

故事的寓意:不要使用原始类型!

  • @Michael:JLS在第4.8节(原始类型)中包含了这个讨论:"Raw类型与通配符紧密相关.两者都基于存在类型.原始类型可以被认为是通配符,其类型规则故意不合理,以适应交互遗留代码." 换句话说,原始类型实际上通常不应出现在新代码中,但他们试图避免使旧代码无法编译,即使它至少是可疑的. (4认同)