1

我有一个问题(简化):

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

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

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

在阅读了一个类似的线程之后,我明白如果我将方法签名重写为:

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

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

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

3 回答 3

5

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

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

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

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


有关的:

于 2013-10-28T19:46:06.517 回答
2

我想我在Angelika Langer 的泛型常见问题解答“案例研究 #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); 
  } 
} 

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

于 2013-10-28T19:38:55.820 回答
1

根本原因是它List<List<?>>不是List<List<String>>.

例如, AList<List<?>>可以包含 aList<Integer>和 a List<String>

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

于 2013-10-28T19:48:57.067 回答