为什么Java编译器在使用foreach与原始类型时会发出警告?
为什么Java编译器在使用foreach与原始类型时会发出警告?
在Java中,当在for-each循环中使用泛型时,我遇到了一个奇怪的编译器错误。这是一个Java编译器的bug,还是我真的漏掉了什么?
这是我的整个类:
public class Generics{ public Generics(T myObject){ //我实际上并不需要myObject } public List getList(){ List list = new ArrayList (); 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); } } }
编译器对于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); } }
我知道`getList()`确实使用了泛型,但我认为它在我认为是完全无关的方式使用泛型。如果我尝试迭代类型为T的某个东西,并且`getList()`返回了`List
请注意,如果我这样做,代码也能编译通过,我本以为这应该与第一个代码等效:
public static void main(String...a){ Generics generics = new Generics(new Object()); Listlist = generics.getList(); for(String s : list){ System.out.println(s); } }
Java编译器在使用foreach循环时为什么会报错?
问题的出现是因为使用了原始类型(raw type)。getList方法的类型是List,而不是List
解决方法是将代码中的`Generics generics = new Generics(new Object());`改为`Generics> generics = new Generics
需要注意的是,Generics不会成为String类型的泛型,这就是问题的根源。无论T的类型如何,getList方法应该返回一个List
如果只是将类声明中的类型参数`
问题的根源是使用了原始类型,解决方法是使用通配符。
为什么Java编译器在使用foreach与原始类型时会报错?
当你使用原始类型时,所有成员签名中的泛型引用都会转换为它们的原始形式。所以实际上你调用的方法具有如下签名:
List getList()
至于为什么你的最终版本可以编译——虽然可以编译,但如果你使用-Xlint
选项编译会有警告:
Generics.java:16: warning: [unchecked] unchecked conversion List<String> list = generics.getList(); ^
这与以下代码类似:
List list = new ArrayList(); List<String> strings = list;
... 这段代码也可以编译,但在-Xlint
下会有警告。
故事的寓意是:不要使用原始类型!
我对于所有成员签名中的泛型引用都被转换为它们的原始形式感到非常惊讶。除了Sun公司觉得这样做之外,还有其他原因吗?
:《Java语言规范》在第4.8节(原始类型)中包含了这个讨论:“原始类型与通配符密切相关。两者都基于存在类型。可以将原始类型看作是类型规则故意不完善的通配符,以适应与旧代码的交互。”换句话说,原始类型通常不应该出现在新代码中,但他们试图避免使旧代码无法编译,即使那样至少是可疑的。
非常有趣。我已经知道要避免使用原始类型(一个同事编写了声明变量的代码),但这突显出它确实很重要。
-mcgowan我猜他们在某个时候对泛型的细节感到厌烦,这就是他们认为可以省些时间的地方。如果他们有更多时间/精力,他们不会把它留在这么邋遢的状态下。
Java编译器为什么会在使用原始类型的foreach循环时抱怨?
在上述代码中,我们可以看到一个名为Generics的类。在main方法中,我们创建了一个Generics对象,并使用foreach循环遍历getList()方法返回的列表。然而,当我们尝试编译这段代码时,Java编译器会抱怨使用原始类型的foreach循环。
这个问题的出现是因为Generics类在声明时没有指定类型参数。原始类型是指没有指定类型参数的泛型类或接口。在使用原始类型的foreach循环时,编译器无法确定要迭代的元素类型,因此会抱怨。
为了解决这个问题,我们需要在Generics类的声明中指定类型参数。在上述代码中,我们可以看到在创建Generics对象时,使用了类型参数String。这样编译器就知道getList()方法返回的是一个String类型的列表,从而能够正确地进行类型检查。
下面是修改后的代码示例:
public class Generics<T> { private List<T> list; public Generics() { this.list = new ArrayList<T>(); } public List<T> getList() { return this.list; } public static void main(String... a) { Generics<String> generics = new Generics<String>(); for (String s : generics.getList()) { System.out.println(s); } } }
在修改后的代码中,我们在Generics类的声明中添加了类型参数T。在getList()方法中,我们使用了泛型列表List<T>,并将其作为返回类型。这样,编译器就能够正确地进行类型检查,而不会抱怨使用原始类型的foreach循环。
通过这样的修改,我们解决了Java编译器抱怨使用原始类型的foreach循环的问题。现在,我们可以使用foreach循环遍历Generics类中的列表,并打印每个元素。这样,我们可以避免编译器错误和潜在的运行时错误。