我正在开发基于Java的Web应用程序,突然发现了一种我目前无法理解的行为。
我们说我有以下代码:
...snip...
Map<Integer, Foo> map = new HashMap<Integer, Foo>();
...snip...
public List<Foo> getFoos() {
return (List<Foo>)map.values();
}
我知道你需要像新的ArrayList()那样实例化一个新的List,但我想知道为什么eclipse没有给我一个警告。
只有在运行代码时我才会获得ClassCastException。我非常肯定这不是eclipse的错误,因为编译器也没有这个代码的问题但是有人可以解释为什么你在运行这段代码时总是会出错,但IDE和编译器不会抱怨?
答案 0 :(得分:5)
IDE和编译器都只在语法级别上进行检查。在语法层面上,一切都很好。由于强制转换,getFoos()
的结果类型属于有效类型List,其他所有内容都是正常的。这实际上是语义上的一个错误。在语义级别上,您试图将map.values()
的结果转换为List,该结果实际上将直接从AbstractCollection派生的内部类型的HashMap实例返回给List。由于这不是有效的强制转换(List不是map.values()
的结果的超类型),因此java将在运行时抛出ClassCastException。
答案 1 :(得分:1)
根据javadoc,Map.values()
会返回Collection<V>
,这意味着无法保证List
。HashMap#values()
。这取决于特定映射的内部实现(在您的情况下,AbstractCollection
方法的实现返回List
接口的实现,称为Values
,而不是 a Collection<Foo>
)。
更好地将您的方法的返回类型更改为{{1}}并删除演员。
答案 2 :(得分:0)
Map.values()
应该返回Collection
。由于所有List
s 都是 Collection
,Map.values
的实现可能会返回List
,并且广播会正确。在编译时,无法证明地图的值Collection
将是List
,但也无法反驳它,因此编译器允许代码,但插入运行时检查。