我创建了一个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()
本身不包含泛型。
根据 第14.14.2节。 Java语言规范,使用迭代器将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
,你声明它 生的 type,从方法的返回类型中删除泛型类型 getLabels
同样:它变得像 List getLabels();
,显然 Object
类型是for循环结构。
在同一时间 Iterator<String> iterator = myEntity.getLabels().iterator();
工作正常,因为您明确指定类型: Iterator<String>
。
非常类似的例子给出了 JLS 4.8“原始类型” 这解释了为什么会发生:
...依赖于类型变量的继承类型成员
由于规则的原因而继承为原始类型
原始类型的超类型被删除...
上述规则的另一个含义是通用的内部类
原始类型本身只能用作原始类型:
class Outer<T>{
class Inner<S> {
S s;
}
}
无法将Inner作为部分原始类型访问(“罕见”
类型):
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
}
}
关于为什么这样做会更加细致 Iterator<String> iterator = myEntity.getLabels().iterator();
JLS的工作基于以下规则:
也就是说,Java编程的子类型规则(第4.10.2节)
语言使得可以分配原始类型的变量
任何类型的参数化实例的值
以同样的方式,你总是可以编写编译良好的代码:
List<String> labels = myEntity.getLabels();
for (String label : labels) { // <-- OK, no error here
/* ... */
}