Java:为什么可以对通配符集合进行强制转换?
假设我们有一个类a和一个从类a继承的类B。 假设我们有:
Set<A> setOfAs = new HashSet<>();
以下铸件:
((Set<B>) setOfAs)
将给我们运行时错误
但是,如果我们使用通配符并定义以下集合:
Set<? extends A> setOfAs = new HashSet<>();
我们在铸造方面没有问题:
((Set<B>) setOfAs)
为什么允许强制转换通配符集合,而禁止强制转换“常规”类型的集合
# 1 楼答案
你会有一个未经检查的演员阵容警告,所以你并不是真的没有问题;只是编译器不能证明它肯定是错的,也不能在字节码中放入任何东西来捕捉它在运行时出错的事实
A
Set<? extends T>
是一个Set
,可以假设所有成员都可以安全地被转换到T
而不需要ClassCastException
A
Set<? super T>
将是一个Set
,在其中添加AT
是安全的,而不会在依赖于Set
中元素类型的地方造成ClassCastException
(我认为正确的技术术语是而不会造成堆污染)A
Set<T>
是这两种有界类型的交集:可以向其中添加T
的实例,其中的所有元素都是T
的实例根据这些定义,
Set<B>
可以充当Set<? extends A>
,因为任何可以转换为B
的东西也可以转换为A
但是,
Set<A>
不能充当Set<B>
,因为它可能包含A
的实例,而这些实例不是B
的实例# 2 楼答案
选角的整个想法就是说“我知道的比你多,编译器!”当物体的实际类型存在一些不确定性时
在第二种情况下,这完全有道理。编译器知道
setOfAs
是Set<? extends A>
类型,这意味着“未知类型的Set
,而未知类型扩展了A
”。关于它可能是什么类型的HashSet
存在不确定性。就编译器而言,可能是HashSet<B>
但它也可能是^{
你,通过施法,说“不,
setOfAs
是aHashSet<B>
”。编译器会说,“嗯,可能是这样,所以我相信你。”。你的额外知识是否真的正确,是另一回事然而,在第一种情况下,
setofAs
是类型HashSet<A>
。由于类型为HashSet<A>
的变量可以从不存储类型为HashSet<B>
的对象,即它不会编译:关于
Set
的泛型参数没有不确定性。一定是A
。你试图转换到HashSet<B>
,只会导致编译器说“不,它永远不会是那样的!”