我创建了一个MWE,其中通过添加<?>
更改单行可解决编译器错误。
以下代码无法编译:
import java.util.List;
public class MainClass {
public void traverse() {
List<MyEntity> list = null /* ... */;
for (MyEntity myEntity : list) {
for (String label : myEntity.getLabels()) { // <-- Offending Line
/* ... */
}
}
}
interface MyEntity<T> {
T get();
List<String> getLabels();
}
}
编译器错误是:
Error:(9, 51) java: incompatible types: java.lang.Object cannot be converted to java.lang.String
将有问题的行中的定义从
MyEntity myEntity
更改为MyEntity<?> myEntity
可解决此问题。我想知道为什么将此for-each的返回类型视为Object
而不是String
,除非将通配符添加到父类中?请注意,getLabels()
本身不包含泛型。根据Chapter 14.14.2. of the Java Language Specification,使用迭代器将for-each编译为循环。有趣的是,手动将for-each扩展为这样的迭代器可以工作:
Iterator<String> iterator = myEntity.getLabels().iterator();
while (iterator.hasNext()) {
String label = iterator.next();
/* ... */
}
谁能解释为什么?
最佳答案
首先,您的代码示例的方法主体可以简化为:
public void traverse() {
MyEntity myEntity = null;
for (String label : myEntity.getLabels()) { // <-- Offending Line
/* ... */
}
}
为什么会这样呢?因为当您将变量
myEntity
(与for循环中的位置无关,在我的示例中)无关紧要作为MyEntity myEntity
时,您将其声明为原始类型,这也从方法getLabels
的返回类型中消除了泛型类型:它变得像List getLabels();
一样,显然Object
类型可用于for循环构建。同时
Iterator<String> iterator = myEntity.getLabels().iterator();
可以正常工作,因为您在此处明确指定类型:Iterator<String>
。JLS 4.8 "Raw types"提供了非常相似的示例,该示例说明了为什么会发生这种情况:
class Outer<T>{
class Inner<S> {
S s;
}
}
Outer.Inner<Double> x = null; // illegal
UPD-2 :当我收到有关
Iterator<String> iterator = myEntity.getLabels().iterator();
的问题时,为什么这样做可以,尽管第一个示例不起作用?我个人认为这看起来令人困惑。但这是规则。此示例在同一JLS段落中也涉及到:
class Cell<E> {
E value;
Cell(E v) { value = v; }
E get() { return value; }
void set(E v) { value = v; }
public static void main(String[] args) {
Cell x = new Cell<String>("abc");
System.out.println(x.value); // OK, has type Object
System.out.println(x.get()); // OK, has type Object
x.set("def"); // unchecked warning
}
}
关于JLS的
Iterator<String> iterator = myEntity.getLabels().iterator();
为什么起作用的更仔细的解释是基于以下规则:以相同的方式,您始终可以编写经过良好编译的代码,如下所示:
List<String> labels = myEntity.getLabels();
for (String label : labels) { // <-- OK, no error here
/* ... */
}
关于java - 省略<?>会直观地破坏该代码,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/42246136/