3

以下内容来自泛型教程:

说类 R 扩展了 S,

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

您应该能够弄清楚为什么不允许上面的代码。s.add() 的第二个参数的类型是?extends 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?

4

2 回答 2

3

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

stype的参数List<? extends S>不仅可以是List<S>or的实例List<R>,还可以是List<T>where Textends S。在这种情况下,即使R也扩展了SR也不一定扩展T(它们可能是,例如类层次结构中的兄弟姐妹)。由于您只能T在这样的集合中放置一个类型的值,因此编译器无法在编译时保证放置一个R存在是安全的。

举一个更具体的例子,你不能将 a 添加Double到 a List<? extends Number>,即使Doubleextends Number!这是因为,例如,可以在运行时为类型变量分配 a List<? extends Number> List<Integer>并且不允许将 a 添加到Double这样的列表中。

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

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

这个总体思路被称为 PECS(生产者-扩展,消费者-超级)。《 Effective Java》的第 5 章(很方便,你可以从本书的网站下载示例章节)对泛型的这一点和其他细微之处进行了更多说明。

于 2009-12-04T07:05:54.423 回答
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());
于 2009-12-04T17:00:53.150 回答