鉴于以下课程......
package whoop.deduper;
/**
* @author deduper
*
*/
public class Foo {
}
package whoop.deduper;
import java.math.BigDecimal;
import java.util.Iterator;
import java.util.List;
/**
* @author deduper
*
*/
public class FuBar {
public int fuBar( ){
Bar bar = new Bar();
List<Foo> foos = bar.foos();
int baz = 0;
for ( Iterator iterator = foos.iterator( ); iterator.hasNext( ); ) {
BigDecimal foo = (BigDecimal) iterator.next( );
baz = foo.intValue( );
return baz;
}
return baz;
}
}
package whoop.deduper;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author deduper
*
*/
public class Bar {
public <T extends Foo> List<T> foos( ) {
List sketchy = new ArrayList();
sketchy.add( BigDecimal.TEN );
return sketchy;
}
}
package whoop.deduper;
import static java.lang.System.out;
/**
* @author deduper
*
*/
public class EffedUp {
/**
* @param args
*/
public static void main( String[ ] args ) {
int effedUp = new FuBar().fuBar( );
out.println(effedUp + " — But it works!");
}
}
...为什么这甚至有效?我的意思是,我本来期望在那里的某个地方有ClassCastException!为什么不存在?
请有人能指出一份清楚的书面解释,说明这里发生了什么吗?
提前致谢。
答案 0 :(得分:1)
拥有List<Foo>
类型的变量并不能保证您实际上有一个Foos列表。你只会确保当你试图将Foos拉出来时,你不是,你的迭代器不是Iterator<Foo>
而你不会在任何地方施放Foo
。如果您让编译器为您进行转换,那么它将按预期中断。
List<Foo> foos = bar.foos();
int baz = 0;
for (Foo foo : foos) {
System.out.println(foo); // ClassCastException
}
如果你使用如上所示的“normal”路径,编译器将自己成为迭代器并将每个元素强制转换为Foo
,因此如果列表不为空,上述代码将失败。
您的代码演示了raw-types有多糟糕以及为什么应该始终避免它们。
答案 1 :(得分:-1)
List l = new ArrayList();
List<Integer> ints = l;
当您完成作业时,您将告诉Java将l
的地址放在ints
中,仅此而已。这种情况发生在运行时,从代码中删除泛型。
当编译器尝试识别与类型相关的可能错误时,编译器在编译时使用泛型。它不会在编译时崩溃,因为他不知道列表中存储了什么值,因为没有指定。当列表包含意外元素时,它会在ClassCastException
运行时崩溃(因为只有在运行时才会有对象)。
我无法弄清楚他们为何在编译时允许这种情况发生的原因。如果编译器遇到类似这样的事情,我会发现编译错误。而不是错误,你会收到警告。
当您完成作业时,您只需说I give you a list, doesn't matter what it has inside, you just store it
。
作为建议,请始终指定列表的类型。在您的情况下List<T>
。请记住这一点,只要您不需要包含许多对象类型的列表,根据我的经验,这种类型很少见。