关于java:Generics<?

Generics <? super A> doesn't allow superTypes of A to be added to the list

我在列表中使用通配符和下界泛型,但编译器抛出了错误。

代码:

1
2
3
4
5
int intStart = 0;      
Number num = new Integer(2);                
List<? super Integer> listOfNumbers = new ArrayList<>();
listOfNumbers.add(intStart);
listOfNumbers.add(num); //throws compiler error

错误:

The method add(capture#8-of ? super Integer) in the type List is not applicable for the arguments (Number)

对于List,应该允许我添加任何Integer类型或其超类型的对象,例如数字或对象。我已经进行了一些这样的讨论,但找不到我为什么要克服这个错误。


With List, I should have been allowed to add any objects of type Integer or its superTypes e.g. Number or Object.

不正确。List并不表示可以容纳任何超类型整数的列表。它表示一个列表,其泛型类型可以是某个特定的整数父类型。所以它实际上可能是ListListList。所有这些列表类型都可以包含一个Integer,但对于Number不能这样说。Number可以是DoubleLong,在List中是不允许的。

要使其更具体一点,请考虑以下片段:

1
2
3
Long longValue = 2L;
List<Integer> listOfIntegers = new ArrayList<>();
// listOfIntegers.add(longValue);

注释行显然不应该编译,也不应该编译。但是如果我们添加以下内容会怎么样:

1
2
3
Number longAsNumber = longValue;
List<? super Integer> listOfSuper = listOfIntegers;
listOfSuper.add(longAsNumber);

这相当于问题中的代码片段。最后一行应该编译吗?如果这样,它将违反List的一般不变量。

对于您的评论,在您的示例中,List确实是不必要的。更常见的情况是为方法参数增加灵活性。例如,一个方法

1
2
3
void addInt(List<Integer> list) {
    list.add(1);
}

只能接受不必要的限制。将参数类型更改为List将允许方法接受ListList,其中任何一个都可以包含Integer值。

这仅在列表使用相关的泛型类型时有效。如果该方法试图从列表中生成值,则会强制假定它们是Object类型,因为我们没有明确的父类型。有关更多信息,请参见什么是PECS(生产者扩展消费者超级)?


List有点奇怪。只能添加整数(int自动装箱为整数)。您不能添加数字,因为List中不允许添加数字,即使ListList的有效分配,因为您可能正在使用List。因为编译器不确定,所以它不允许不安全的操作。

因此,简而言之,当使用List时,唯一可安全读取的类型是object,唯一可安全写入的类型是integer。编译器不允许这样做,因为它是一个不安全的操作。

(为了进一步阅读,这个答案给出了一个很好的详细解释,说明了这一点。)