为什么只允许在某些地方传递子类到有界通配符?

以下内容来自generics教程:

说R类扩展S,

public void addR(List s) { s.add(0, new R()); // Compile-time error! } 

您应该能够弄清楚为什么不允许上面的代码。 s.add()的第二个参数的类型是? 扩展S – 一个未知的S子类型。因为我们不知道它是什么类型,我们不知道它是否是R的超类型; 它可能是也可能不是这样的超类型,所以在那里传递R是不安全的。

我已经阅读了几次,但我仍然不太明白为什么以下是一个错误

鉴于List.add()的签名

 void add(int index, E element) 

不等于

 void add(int index,  element) // just to explain the idea, not a valid syntax 

为什么这是一个错误调用add(0,new R())R是一个S?

以下是斜体文字所指的内容:

参数s ,类型为List List ,不仅可以是ListList的实例,还可以是List ,其中T扩展为S 在这种情况下,即使R也扩展SR也不一定扩展T (它们可以是,例如类层次结构中的兄弟)。 由于您只能在这样的集合中放入T类型的值,编译器无法保证在编译时放置R会安全。

为了给出一个更具体的例子,你不能将Double添加到List List ,即使Double扩展Number ! 这是因为List的变量List 例如, List 可以在运行时分配List ,并且不允许向这样的列表添加Double

实际上,您实际上无法调用声明为List的列表的add方法List List ,因为在运行时,通配符总是表示S某个子类型,它不是您要添加的事物的超类。 但是,您可以从这样的列表中读取 ,因为它保证了通配符是S的子类型,因此可以分配给S类型的变量:

 public S getElement(List s) { S result = s.get(0); return result; } 

这个一般的想法被称为PECS(生产者扩展,消费者超级)。 有效Java的第5章(很方便,这是你可以从本书的网站上下载的样本章节)对于generics的这个和其他细微之处有更多的说法。

想象这将是最简单的解释

class级结构:

 public class List {} public class S {} public class R extends S {} public class T extends R {} 

代码用法:

 List list = new List(); 

在这种情况下,以下内容无效:

 list.add(new R());