为什么只允许在某些地方传递子类到有界通配符?
以下内容来自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 extends S>
List extends S>
,不仅可以是List
或List
的实例,还可以是List
,其中T
扩展为S
在这种情况下,即使R
也扩展S
, R
也不一定扩展T
(它们可以是,例如类层次结构中的兄弟)。 由于您只能在这样的集合中放入T
类型的值,编译器无法保证在编译时放置R
会安全。
为了给出一个更具体的例子,你不能将Double
添加到List extends Number>
List extends Number>
,即使Double
扩展Number
! 这是因为List extends Number>
的变量List extends Number>
例如, List extends Number>
可以在运行时分配List
,并且不允许向这样的列表添加Double
。
实际上,您实际上无法调用声明为List extends S>
的列表的add
方法List extends S>
List extends S>
,因为在运行时,通配符总是表示S
某个子类型,它不是您要添加的事物的超类。 但是,您可以从这样的列表中读取 ,因为它保证了通配符是S
的子类型,因此可以分配给S
类型的变量:
public S getElement(List extends S> s) { S result = s.get(0); return result; }
这个一般的想法被称为PECS(生产者扩展,消费者超级)。 有效Java的第5章(很方便,这是你可以从本书的网站上下载的样本章节)对于generics的这个和其他细微之处有更多的说法。
想象这将是最简单的解释
class级结构:
public class List extends S> {} public class S {} public class R extends S {} public class T extends R {}
代码用法:
List list = new List ();
在这种情况下,以下内容无效:
list.add(new R());