以下内容来自泛型教程:
说R类延伸S,
public void addR(List<? extends S> 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, <? extends S> element) // just to explain the idea, not a valid syntax
为什么是错误调用add(0,new R())R是S?
答案 0 :(得分:3)
以下是斜体文字所指的内容:
s
类型的参数List<? extends S>
不仅可以是List<S>
或List<R>
的实例,还可以是List<T>
T
} extends S
。在这种情况下,即使R
也扩展S
,R
也不一定会扩展T
(它们可能是,例如类层次结构中的兄弟姐妹)。由于您只能在这样的集合中放置T
类型的值,编译器无法保证在编译时将R
放在那里是安全的。
为了给出更具体的示例,即使Double
扩展List<? extends Number>
,也无法向Double
添加Number
!这是因为List<? extends Number>
类型的变量可以,例如,在运行时被分配List<Integer>
,并且不允许向这样的列表添加Double
。
实际上,您实际上无法调用声明为add
的列表的List<? extends S>
方法,因为在运行时,通配符始终表示某个S
的子类型,而不是'{1}}你要添加的东西的超类。但是,您可以从这样的列表读取,因为它保证通配符是S
的子类型,因此可以分配给S
类型的变量:
public S getElement(List<? extends S> s) {
S result = s.get(0);
return result;
}
这个一般的想法被称为PECS(生产者延伸,消费者超级)。 Effective Java的第5章(很方便,这是你可以从书籍网站上下载的样本章节),对于这个以及其他微妙的泛型有更多的说法。
答案 1 :(得分:0)
想象这将是最简单的解释
班级结构:
public class List<? extends S> {}
public class S {}
public class R extends S {}
public class T extends R {}
代码用法:
List<T> list = new List<T>();
在这种情况下,以下内容无效:
list.add(new R());