【问题标题】:How to instantiate generics using wild card?如何使用通配符实例化泛型?
【发布时间】:2014-10-01 16:46:03
【问题描述】:

让我们研究一些使用通配符的通用实例化情况:

1

这段代码

List<?> list = new ArrayList<?>();

产生以下错误:

required: class or interface without bounds
found:    ?

2

但是这个

List<?> list = new ArrayList< Set<?> >();

编译成功。

3

还有这个:

List<Set<?>> list = new ArrayList< Set<?> >();

编译也成功了。

4

但是这个:

List<Set<Map<?,?>>> list = new ArrayList< Set<Map<String,String>> >();

生成

required: List<Set<Map<?,?>>>
found:    ArrayList<Set<Map<String,String>>>

5

List<Set<?>> list = new ArrayList< HashSet<?> >();

生成

required: List<Set<?>>
found:    ArrayList<HashSet<?>>

我对这些输出感到非常困惑。

我看到以下规律:

我只能在第一级从左侧部分到右侧部分替换?,并且类型应该在 内部相同,只是?和 ?是禁止的。

但我不明白为什么?

您能否提供如何使用通配符实例化泛型的通用规则?

【问题讨论】:

标签: java generics wildcard instantiation


【解决方案1】:
  1. 您不能使用通配符直​​接实例化一个类型。实例化时类型参数需要是实际类型,因此会产生编译器错误。

代码:

List<?> list = new ArrayList<?>();
  1. 以下编译成功。

代码:

List<?> list = new ArrayList< Set<?> >();

您可以使用通配符作为类型参数的泛型类型参数,例如Set&lt;?&gt;,一组任何东西。此外,任何类型参数都将匹配左侧的通配符 ?

  1. 这也成功编译:

代码:

List<Set<?>> list = new ArrayList< Set<?> >();

类型参数匹配,? 没有像上面的(1)那样直接使用。

  1. 以下内容无法编译:

代码:

List<Set<Map<?,?>>> list = new ArrayList< Set<Map<String,String>> >();

这是因为即使 Map&lt;String, String&gt;Map&lt;?, ?&gt;List&lt;Set&lt;Map&lt;String, String&gt;&gt;&gt; 也不是 List&lt;Set&lt;Map&lt;?, ?&gt;&gt;&gt;。 Java 泛型是不变的,这意味着类型参数必须匹配; “is-a”关系必须在上限中使用通配符明确指定。例如。通过在左侧引入上限通配符来编译此更改。

List<? extends Set<? extends Map<?,?>>> list = new ArrayList< Set<Map<String,String>> >();
  1. 由于与上述 (4) 相同的原因,以下内容无法编译:

代码:

List<Set<?>> list = new ArrayList< HashSet<?> >();

即使HashSet&lt;?&gt;Set&lt;?&gt;,由于Java 的不变泛型,ArrayList&lt;HashSet&lt;?&gt;&gt; 也不是List&lt;Set&lt;?&gt;&gt;。在左侧引入通配符作为上限也适用:

List<? extends Set<?>> list = new ArrayList< HashSet<?> >();

【讨论】:

【解决方案2】:
  1. 您不能使用通配符进行实例化。您必须指定类型。 “?”不是类型。
  2. 这没关系,因为你给List&lt;?&gt; 一个类型,即Set&lt;?&gt;Set&lt;?&gt; 是一种类型。
  3. 正确。 ArrayList&lt;T&gt;List&lt;T&gt; 的子类型。
  4. 这就是事情变得丑陋的地方。右手不是左手的子类型,因为 Java 处理类型参数的方式很奇怪。类型参数必须完全匹配,或者您必须使用协方差/逆变(&lt;A extends B&gt;&lt;A super B&gt; 的东西)。 List&lt;String&gt; 不是 List&lt;Object&gt; 的子类型。它List&lt;? extends Object&gt; 或只是List&lt;?&gt; 的子类型。
  5. 同上。如果您将其声明为 List&lt;? extends Set&lt;?&gt;&gt;,它将起作用。

【讨论】:

  • "ArrayList&lt;T&gt;List&lt;T&gt;"的子类型 不,ArrayList&lt;T&gt; 实现 List&lt;T&gt;
  • Java 在处理案例 4 或案例 5 方面并不奇怪。它是完全一致的。如果list 的类型是List&lt;Set&lt;Map&lt;?,?&gt;&gt;&gt;,那么我可以附加到Collections.singleton(Collections.SingletonMap(new Object(), new Object()))。但是对于类型为ArrayList&lt;Set&lt;Map&lt;String,String&gt;&gt;&gt; 的对象,该元素是不可接受的。因此,将后一种类型的对象分配给前一种类型的变量不是类型安全的。
  • @user1803551:因为您可以将ArrayList&lt;T&gt; 分配给List&lt;T&gt;,所以子类型(IS-A)关系。不是子,而是子类型
  • @JanVandenbosch 我必须检查 JLS。根据 4.10,ArrayList&lt;T&gt;List&lt;T&gt; 的子类型,仅当 List&lt;T&gt;ArrayList&lt;T&gt; 的超类型时。根据 4.10.2,ArrayList&lt;T&gt; 的超类型将是 ArrayList&lt;T&gt;超类 或原始类型 ArrayList。这些不包括List&lt;T&gt;。我不认为“is-a”与“subtype”相同。
猜你喜欢
  • 1970-01-01
  • 2022-01-18
  • 1970-01-01
  • 2011-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多