Java 省略<&燃气轮机;非直觉地破坏此代码

Java 省略<&燃气轮机;非直觉地破坏此代码,java,generics,foreach,Java,Generics,Foreach,我创建了一个MWE,其中通过添加更改单行可以解决编译器错误 以下代码未编译: import java.util.List; public class MainClass { public void traverse() { List<MyEntity> list = null /* ... */; for (MyEntity myEntity : list) { for (String label : myEntit

我创建了一个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();
    }
}
将有问题的行中的定义从
MyEntity MyEntity
更改为
MyEntity MyEntity
可以解决此问题。我想知道,除非我将通配符添加到父类中,否则为什么每个对象的返回类型都被视为
对象
,而不是
字符串
?请注意,
getLabels()
本身不包含泛型

根据,使用迭代器将for-each编译为循环。有趣的是,手动将for each扩展为这样一个迭代器是有效的:

Iterator<String> iterator = myEntity.getLabels().iterator();
while (iterator.hasNext()) {
    String label = iterator.next();
    /* ... */
}
Iterator Iterator=myEntity.getLabels().Iterator();
while(iterator.hasNext()){
字符串标签=迭代器.next();
/* ... */
}

有人能解释一下原因吗?

首先,代码示例的方法体可以简化为:

public void traverse() {
    MyEntity myEntity = null;
    for (String label : myEntity.getLabels()) { // <--  Offending Line
            /* ... */
    }
}
不可能以部分原始类型(罕见类型)访问内部 类型):

Outer.Inner x=null;//非法的

UPD-2:当我收到关于
Iterator 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
    }
}
类单元{
E值;
单元(EV){value=v;}
E get(){返回值;}
无效集(EV){value=v;}
公共静态void main(字符串[]args){
单元x=新单元(“abc”);
System.out.println(x.value);//好,有类型对象
System.out.println(x.get());//好,有类型对象
x、 set(“def”);//未选中警告
}
}

更仔细地解释为什么
Iterator Iterator=myEntity.getLabels().Iterator()JLS的工作基于此规则:

也就是说,Java编程的子类型规则(§4.10.2) 语言使原始类型的变量可以被赋值 任何类型的参数化实例的值

同样,您也可以编写编译良好的代码,如下所示:

    List<String> labels = myEntity.getLabels();
    for (String label : labels) { // <-- OK, no error here
            /* ... */
    }
List labels=myEntity.getLabels();

对于(stringlabel:labels){//Nice one,以前从未见过,谢谢!不过我需要一些进一步的澄清:假设,如您所述,将
myEntity
声明为raw也会使所有成员都是raw,那么我如何将
myEntity.getLabels()的结果存储在(非raw!)中
Iterator
?这难道不应该是非法的吗?情况有点不同。
Iterator Iterator()
包含类型变量,而不是泛型类型。当您在
Iterator Iterator
中指定此类型时,实际上就是“还原”这个类型,这就是为什么它工作的原因,它本身就是原始类型。但是,因为它不是直接指定类型的方法,所以它会给你编译时未检查的警告。
list labels=myEntity.getLabels()
那么编译器就可以了。这是因为赋值中允许某种类型的强制吗?@FabienBenoit Koch请看我的更新,也许它可以给你一些帮助clue@Andremoniy“恢复”您的部分评论为我澄清了这一点。因此,感谢您的回答,我了解到声明一个原始类型会使其所有成员都是原始的,甚至是与类本身的类型参数完全不相关的成员。谢谢!!
Outer.Inner<Double> x = null; // illegal
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
    }
}
    List<String> labels = myEntity.getLabels();
    for (String label : labels) { // <-- OK, no error here
            /* ... */
    }