【问题标题】:Generic method vs wildcard - compilation error通用方法与通配符 - 编译错误
【发布时间】:2013-11-07 17:36:59
【问题描述】:

我有一个问题(为了简化):

public void method(List<List<?>> list){...}

调用时给了我一个编译错误:

method(new ArrayList<List<String>>()); // This line gives the error

阅读similar thread后,我明白如果我将方法签名重写为:

public void method(List<? extends List<?>> list){...}

现在,我的问题是,为什么下面的工作那么有效?

public <T> void method(List<List<T>> list){...}

【问题讨论】:

标签: java generics


【解决方案1】:

当您处理多级通配符语法时,确实会出现困惑。让我们了解这些类型在其中的确切含义:

  • List&lt;List&lt;?&gt;&gt; 是一个具体的参数化类型。它是不同类型List&lt;E&gt;异构集合。由于List&lt;?&gt; 代表List 的所有实例化的家族,因此您不能真正将ArrayList&lt;List&lt;String&gt;&gt; 传递给List&lt;List&lt;?&gt;&gt;。因为,没有什么能阻止你在方法中添加List&lt;Integer&gt;,如果编译器允许的话,它会在运行时崩溃。

  • List&lt;? extends List&lt;?&gt;&gt; 是一个通配符参数化类型。它代表一系列不同类型的List&lt;E&gt;。基本上,它可能是List&lt;ArrayList&lt;String&gt;&gt;List&lt;LinkedList&lt;Date&gt;&gt;,等等。它可以是从List&lt;?&gt; 扩展的任何类型的列表。因此,将ArrayList&lt;List&lt;String&gt;&gt; 传递给它是安全的,原因是,您将不能添加任何东西,但null 将被添加到列表中。向列表中添加任何内容都会导致编译时错误。

  • 至于List&lt;List&lt;T&gt;&gt;,它又是一个具体的参数化类型。由于您现在处理的是泛型方法,因此类型参数将被推断为为其传递的类型。因此,对于ArrayList&lt;List&lt;String&gt;&gt;,类型T 将被推断为T。泛型方法处理用它声明的类型。所以,这里只有一个类型T。您从List&lt;List&lt;T&gt;&gt; 中获得的所有列表肯定是List&lt;T&gt;,适用于任何类型T。所以,它是那种List同质集合。在方法内部,您不能将任意List&lt;E&gt; 添加到List&lt;List&lt;T&gt;&gt;,因为编译器不知道该类型E 是否与T 兼容。所以,这是安全的调用。


相关:

【讨论】:

  • @PaulBellora 你是对的。我已经对答案进行了一些编辑。现在好了。感谢您指出。 :)
  • 糟糕,我的错误。对不起,@PaulBellora。我正在删除我之前的评论。
【解决方案2】:

我想我在Angelika Langer's generics FAQ,“案例研究#3”中找到了答案:

如果方法签名使用多级通配符类型,则通用方法签名和通配符版本之间总是存在差异。这是一个例子。假设有一个通用类型 Box,我们需要声明一个方法来获取一个盒子列表。

示例(带有类型参数的方法):

public static  <T> void print1( List <Box<T>> list) { 
  for (Box<T> box : list) { 
    System.out.println(box); 
   } 
} 

示例(带有通配符的方法):

public static void print2( List <Box<?>> list) { 
  for (Box<?> box : list) { 
    System.out.println(box); 
  } 
} 

这两种方法都是表现良好的方法,但它们并不等价。通用版本需要相同类型的盒子的同质列表。通配符版本接受不同类型框的异构列表。这在调用两个打印方法时变得可见。

【讨论】:

    【解决方案3】:

    基本原因是List&lt;List&lt;?&gt;&gt;不是List&lt;List&lt;String&gt;&gt;的超类。

    例如,List&lt;List&lt;?&gt;&gt; 可以包含 List&lt;Integer&gt;List&lt;String&gt;

    泛型类型必须完全匹配,否则您可能会得到错误的分配。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多