以下是来自Generics教程的:
假设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?
以下是斜体文本所指的内容:
List extends S>类型的参数s不仅可以是List或List的实例,而且可以是List的实例,其中T扩展s。在这种情况下,即使R也扩展了s,R也不一定扩展了T(它们可能是,例如类层次中的兄弟姐妹)。由于在这样的集合中只能放置类型为T的值,因此编译器无法保证在编译时将R放置在该集合中是安全的。
为了给出一个更具体的例子,即使Double扩展了Number,也不能将Double添加到List extends Number>中!这是因为类型为List extends Number>的变量可以在运行时被分配一个List,并且不允许在这样的列表中添加Double。
实际上,您不能实际调用声明为List extends S>的列表的add方法,因为在运行时,通配符始终可以表示s的某些子类型,而这些子类型不是您要添加的内容的超类。但是,您可以从这样的列表中读取,因为它保证通配符是s的子类型,因此可以分配给s类型的变量:
public S getElement(List extends S> s) {
S result = s.get(0);
return result;
}
这个一般概念被称为PECS(生产者扩展,消费者超级)。有效Java的第5章(方便地说,它是你可以从书的网站下载的示例性章节)有更多的关于这个和其他微妙的泛型的说法。
等等,T和R是类层次结构中的兄弟,也是S,为什么您只能将T放在这样的集合中,而不能放在R中?感谢链接到有效的Java。
我添加了一个更具体的例子。希望这能有所帮助。
那么如果它包含一个list和一个list呢?它是一个非参数化的异类列表,如JDK1.4之前的列表。GET仍然可以正常工作,因为您正在分配给一个数字类型变量。
+我指对了方向。
菲尔解释得很好!参数类型可以声明为list,但在运行时传递给方法的实际值始终是list、list、list或list。这就是为什么编译器不相信向这个列表中添加一个新的r()是一个好主意。
"你不能真正调用add方法"你可以调用它并传递null。
我想这是最简单的解释
类结构:
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());
。