Generics与遗留代码的兼容性-为什么foreach在运行时失败,而迭代器工作正常



我有以下测试代码。我正在努力理解Generics和legacy之间的互操作性。

List myList = new ArrayList();
myList.add("abc");
myList.add(1);
myList.add(new Object());
System.out.println("Printing the unchecked list");
Iterator iterator = myList.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
List<String> strings = myList;
System.out.println("Printing the unchecked list assigned to List<String> using iterator");
Iterator stringIterator = strings.iterator();
while (stringIterator.hasNext()) {
System.out.println(stringIterator.next());  // this works fine! why? shouldn't it fail converting the 1 (int/Integer) to String?
}
System.out.println("Printing the unchecked list assigned to List<String> using for");
for (int i = 0; i != strings.size(); i++) {
System.out.println(strings.get(i));  // blows up as expected in the second element, why?
}
System.out.println("Printing the unchecked list assigned to List<String> using foreach");
for (String s : strings) {
System.out.println(s);  // blows up as expected in the second element, why?
}

为什么当我尝试打印iterator.next时,它工作得很好,而当我使用for循环迭代时,System.out.println会像预期的那样爆炸?

关于泛型,需要记住的关键是它只是一种从源代码中省略强制转换的方法。强制转换由编译器插入。因此,由于存在(或不存在(强制转换,代码会失败(或不会失败(。

  1. 这很好用!为什么

因为stringIterator是原始的,所以stringIterator.next()不会强制转换为任何内容:它只是读作Object,这是已擦除的返回类型。

  1. 在第二个元素中按预期爆炸,为什么

stringsList<String>,因此假定strings.get(i)的结果为String,并选择调用println(String),而不是println(Object)。因此,将插入一个铸件。strings.get(1)不是String,因此使用ClassCastException会失败。

有趣的是,如果您使用List<Integer>尝试此操作,则不会失败,因为会调用println(Object),并且不需要强制转换。

  1. 在第二个元素中按预期爆炸,为什么

因为插入了对String的强制转换,所以要将元素分配给String s

关于增强型for回路的减温形式,请参阅JLS 14.14.2:

for (I #i = Expression.iterator(); #i.hasNext(); ) {
{VariableModifier} TargetType Identifier =
(TargetType) #i.next();
Statement
}

因此,您的代码等效于:

for (Iterator<String> it = strings.iterator(); it.hasNext(); ) {
String s = (String) it.next();  // Actually, your code fails on this line.
System.out.println(s);
}

相关内容

最新更新