Java:为什么可以在通配符集合上进行转换?

Cra*_*hax 5 java

假设我们有一个类 A 和一个从类 A 继承的类 B。假设我们有:

Set<A> setOfAs = new HashSet<>();
Run Code Online (Sandbox Code Playgroud)

以下铸造:

((Set<B>) setOfAs)
Run Code Online (Sandbox Code Playgroud)

会给我们运行时错误。

但是,如果我们使用通配符并定义以下集合:

Set<? extends A> setOfAs = new HashSet<>();
Run Code Online (Sandbox Code Playgroud)

我们没有问题进行铸造:

((Set<B>) setOfAs)
Run Code Online (Sandbox Code Playgroud)

为什么允许转换通配符集合,而禁止转换“常规”类型的集合?

And*_*ner 5

我们没有问题进行铸造:

您将收到未经检查的强制转换警告,因此您并不是真的没有问题;只是编译器无法证明它肯定是错误的,并且无法在字节码中添加任何内容以在运行时发现它是错误的事实。

ASet<? extends T>是 a Set,可以假设所有成员都可以安全地转换为T没有 a ClassCastException

ASet<? super T>将是一个Set已知安全的地方,T向它添加 a而不会导致ClassCastException依赖于中元素类型的地方Set(我相信正确的技术术语是不会造成堆污染)。

ASet<T>是这两种有界类型的交集:可以向其添加 的实例T,其中的所有元素都是 的实例T

根据这些定义, aSet<B>可以充当 a Set<? extends A>,因为任何可以转换为 an 的东西B也可以转换为A

但是, aSet<A>不能充当Set<B>,因为它可能包含的实例A不是 的实例B