有人可以向我解释为什么明确需要为ForEachLoop实例分配泛型类型?
为什么编译器会抱怨: 类型不匹配:无法从元素类型Object转换为String ?
JDK 1.5.0_09
import java.util.ArrayList;
import java.util.Collection;
public class ForEachLoop<T> {
public static void main(String[] args) {
// Non functional version
ForEachLoop f = new ForEachLoop();
// Functional version
//ForEachLoop<Integer> f = new ForEachLoop();
// Type mismatch: cannot convert from element type Object to String
for(String a : f.getStrings()) {
System.out.println(a);
}
}
public Collection<String> getStrings() {
Collection<String> strings = new ArrayList<String>();
strings.add("Hello");
return strings;
}
}
答案 0 :(得分:10)
这是一个相当常见的错误:
ForEachLoop f = new ForEachLoop();
应该是
ForEachLoop<Something> f = new ForEachLoop<Something>();
如果您使用原始类型(您不应该使用),编译器将清除该实例的所有通用信息,即使它不是类型参数T,以使其与1.5之前的代码兼容。
如果您正在为Java 1.4或更低版本编写,则只使用原始类型,在这种情况下,您不应该使用任何泛型。在字节码级别,该方法在类型擦除后返回Collection(raw)。通常,如果实例具有泛型类型集,当您尝试对集合执行get
时,编译器将使用通用信息来确定它应该返回一个String,然后在字节码级别它会自动转换它从Collection到String接收的对象(因为它保证是一个String)。但是如果使用原始类型,编译器将忽略所有通用信息,并且不会再为您自动转换对象。
修改:在原始类型部分中有以下内容:
上述规则的另一个含义 是原始的通用内部类 type本身只能用作raw 类型:
class Outer<T>{ class Inner<S> { S s; } }
无法访问Inner as 部分原始类型(“稀有”类型)
Outer.Inner<Double> x = null; // illegal Double d = x.s;
因为外部本身是原始的,所以也是 所有内在的类,包括 内心,所以不可能 将任何类型的参数传递给它。
仅允许使用原始类型 作为对兼容性的让步 遗留代码。使用原始类型 引入后编写的代码 Java编程的通用性 强烈建议不要使用语言。的它 是未来版本的可能 Java编程语言会 不允许使用原始类型。
尝试编译时错误 使用a的类型成员 参数化类型作为原始类型。
这意味着禁止“罕见” 类型延伸到的情况 限定类型是参数化的,但是 我们尝试使用内部类作为 原始类型:
Outer<Integer>.Inner x = null; // illegal
这与我们的情况相反 上面讨论过。没有实际意义 这一半烘焙的理由 类型。在遗留代码中,没有类型 使用参数。在非遗产 代码,我们应该使用泛型类型 正确并传递所有必需的 实际类型参数。
请注意,Inner类具有独立于Outer类的自己的类型参数,并且它仍然被删除。基本上他们不希望我们在同一个实例上混合原始类型和泛型类型,因为它在任何版本中都没有意义(在1.5之前,通用部分将是一个错误,在1.5+原始类型不鼓励,并且甚至可以从将来的版本中删除)
然后还有这个:
构造函数的类型(§8.8), 实例方法(§8.8,§9.4),或 非静态场(§8.3)原始的M 类型C不是从它继承的 超类或超级接口是 擦除它在通用中的类型 宣言对应C. 原始类型的静态成员的类型 C与其中的类型相同 对应的通用声明 下进行。
传递编译时错误 实际的类型参数为非静态 不是原始类型的类型成员 继承自其超类或 超接口。
表示构造函数,实例方法和非静态字段将在原始实例中被视为raw。无论如何,静态成员将被视为通用成员,因为它们不需要加入实例。
答案 1 :(得分:1)
循环的等效表达式是(根据java语言规范):
for (Iterator<String> i = f.getStrings().iterator(); i.hasNext(); ) {
String a = i.next();
System.out.println(a);
}
ForEachLoop是一个泛型类,但f
使用了原始类型。因此编译器必须假设迭代器可以返回Object
个实例或子类型的实例。并发出警告,因为您将原始迭代器分配给参数化迭代器。
只要你的ForEachLoop实例中的集合只包含字符串,你就不会看到运行时错误。
要消除警告,请对实例化部分进行参数化,例如:
ForEachLoop<String> f = new ForEachLoop<String>();
答案 2 :(得分:0)
ForEachLoop<String> f = new ForEachLoop<String>();
答案 3 :(得分:0)
您对非功能版本的期望是什么?您已经声明了一个模板类ForEachLoop<T>
,这是一个不完整的类型,直到完全限定。这不会因为
答案 4 :(得分:0)
Java Generics是使用Type Erasure实现的,这意味着编译器将为泛型类型(例如ForEachLoop<Something>
)的每个实例化发出代码,这些实例基本上使用“强制转换为对象”的习惯用法。因此,您的第ForEachLoop f = new ForEachLoop();
行将生成一个“直接”实例化,其中type参数默认为object
。
所以你真正拥有的是ForEachLoop<object>
,这就是你得到施法错误的原因。