我创建了一个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/

10-11 23:46