【问题标题】:Java: why is casting possible on wildcard collections?Java:为什么可以对通配符集合进行强制转换?
【发布时间】:2020-10-15 16:39:51
【问题描述】:

假设我们有一个类 A 和一个类 B,它继承自类 A。 假设我们有:

Set<A> setOfAs = new HashSet<>();

以下铸造:

((Set<B>) setOfAs)

会给我们运行时错误。

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

Set<? extends A> setOfAs = new HashSet<>();

我们可以毫无问题地进行选角:

((Set<B>) setOfAs)

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

【问题讨论】:

  • “我们没有问题”你有一个未经检查的演员,不是吗? Set&lt;? extends A&gt; 可能是 Set&lt;B&gt;;但是Set&lt;A&gt; 不能同时是Set&lt;B&gt;
  • "会给我们一个运行时错误" — 我认为你的意思是一个编译时错误。
  • 简而言之:在第一个代码sn-p中,编译器知道代码总是错误的(即Set&lt;A&gt;永远不能转换为Set&lt;B&gt;);在第二个代码 sn-p 中,可能会出现这种情况有效,因此编译器只会发出警告,但不会发出错误。

标签: java


【解决方案1】:

铸造的整个想法是说“我比你知道的多,编译器!”当对象的实际类型存在一些不确定性时。

在第二种情况下,这完全有道理。编译器知道setOfAsSet&lt;? extends A&gt; 类型,这意味着“未知类型的Set,并且该未知类型扩展A”。不确定HashSet 的类型。就编译器而言,它可能HashSet&lt;B&gt;...

Set<? extends A> setOfAs = new HashSet<A>();

但它也可以HashSet&lt;A&gt;...

Set<? extends A> setOfAs = new HashSet<B>();

你通过强制转换说“不,setOfAs HashSet&lt;B&gt;”。编译器会说:“嗯,可能就是这样,所以我相信你”。你的额外知识是否真的正确,是另一回事。

然而,在第一种情况下,setofAs 的类型为 HashSet&lt;A&gt;。由于HashSet&lt;A&gt; 类型的变量从不存储HashSet&lt;B&gt; 类型的对象,即这不会编译:

Set<A> setOfAs = new HashSet<B>();

Set 的泛型参数没有不确定性。必须是A。您尝试转换为HashSet&lt;B&gt;,只会导致编译器说“不,它不可能永远那样!”

【讨论】:

    【解决方案2】:

    我们可以毫无问题地进行选角:

    您将收到未经检查的演员表警告,因此并非真的没有问题;只是编译器无法证明它肯定是错误的,也无法在字节码中放入任何东西来捕捉运行时错误的事实。

    Set&lt;? extends T&gt;Set,可以假设所有成员都可以安全地转换为 T,而无需 ClassCastException

    Set&lt;? super T&gt; 将是 Set,已知在其中添加 T 是安全的,而不会在依赖于 Set 中元素类型的地方导致 ClassCastException(我相信正确的技术术语是不会造成堆污染)。

    Set&lt;T&gt; 是这两种有界类型的交集:您可以在其中添加T 的实例,其中的所有元素都是T 的实例。

    根据这些定义,Set&lt;B&gt; 可以充当Set&lt;? extends A&gt;,因为任何可以转换为B 的东西也可以转换为A

    但是,Set&lt;A&gt; 不能充当Set&lt;B&gt;,因为它可能包含不是B 实例的A 实例。

    【讨论】:

      猜你喜欢
      • 2019-03-12
      • 2017-11-26
      • 2023-03-12
      • 1970-01-01
      • 2016-07-14
      • 1970-01-01
      • 2014-11-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多