通用方法与通配符 - 编译错误

时间:2013-10-28 19:26:47

标签: java generics

我遇到了一个问题(简化):

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){...}

3 个答案:

答案 0 :(得分:5)

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

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

  • List<? extends List<?>>通配符参数化类型。它代表了一系列不同类型的List<E>。基本上,它可能是List<ArrayList<String>>List<LinkedList<Date>>,依此类推。它可以是从List<?>扩展的任何类型的列表。因此,将ArrayList<List<String>>传递给它是安全的,原因是,您不会被允许添加任何内容,只有null到列表中。向列表中添加任何内容都将是编译时错误。

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


相关:

答案 1 :(得分: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); 
  } 
} 
  

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

答案 2 :(得分:1)

基本原因是List<List<?>>不是List<List<String>>的超类。

List<List<?>>可以包含List<Integer>List<String>

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