generics不允许将A的超类型添加到列表中

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

码:

int intStart = 0; Number num = new Integer(2); List listOfNumbers = new ArrayList(); listOfNumbers.add(intStart); listOfNumbers.add(num); //throws compiler error 

错误:

类型List中的方法add(capture#8-of?super Integer)不适用于参数(Number)

List List ,我应该被允许添加Integer类型的任何对象或其超类型,例如Number或Object。 我经历了一些SO讨论,但无法找到为什么我应该犯错误。

List List ,我应该被允许添加Integer类型的任何对象或其超类型,例如Number或Object。

那是不对的。 List List并不意味着可以包含任何超类型Integer的列表 。 它表示一个列表,其generics类型可能是某个特定的Integer超类型 。 所以它实际上可能是ListListList 。 所有这些列表类型都可能包含一个Integer ,但对Number却不一样。 Number可能是DoubleLong ,在List是不允许的。

为了使它更具体,请考虑以下片段:

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

注释行显然不应该编译,也不应该编译。 但是,如果我们添加了这个:

 Number longAsNumber = longValue; List listOfSuper = listOfIntegers; listOfSuper.add(longAsNumber); 

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


在回复您的评论时, List 你的例子中确实没有List 。 更常见的情况是增加方法参数的灵活性。 例如,一种方法

 void addInt(List list) { list.add(1); } 

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

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

List List有点奇怪。 您只能添加整数(int获取自动装箱到整数)。 您不能添加数字,因为List不允许这样,即使ListList的有效赋值List List ,因为您可能正在使用List 。 由于编译器不确定,它不会允许不安全的操作。

所以简而言之,当使用List List ,唯一可以安全读取的类型是Object,唯一可以安全写入的类型是Integer。 编译器不允许它,因为它是一个不安全的操作。

(有关进一步阅读, 这个答案给出了相关的generics的详细解释)