Possible Duplicate:
what is the difference between ‘super’ and ‘extends’ in Java Generics
一个)
List<? super Shape> shapeSuper = new ArrayList<Shape>();
shapeSuper.add(new Square()); //extends from SHAP
shapeSuper.add(new DoubleSquare()); //extends from SQ
shapeSuper.add(new TripleSquare()); //extends from DS
shapeSuper.add(new Rectangle()); //extends from SHAP
shapeSuper.add(new Circle()); //extends from SHAP
for (Object object : shapeSuper) { ... }
当我只能添加 Shape 及其对象时,为什么迭代必须是 Objects
衍生品?
B)
List<? super Shape> shapeSuper = new ArrayList<Object>();
shapeSuper.add(new Object()); //compilation error
为什么上述行会产生编译错误?
对于您的示例,您可以使用普通的 List<Shape>
正如丹和保罗所说;您不需要使用通配符问号语法,例如 List<? super Shape>
或 List<? extends Shape>
).我认为您的潜在问题可能是“我什么时候使用问号样式声明之一?” (Julien 引用的 Get and Put Principle 是这个问题的一个很好的答案,但我认为它没有多大意义,除非你在示例的上下文中看到它。)这是我对 Get and Put 的扩展版本的看法提出何时使用通配符的原则。
使用 <? extends T>
如果……
- 一个方法有一个泛型类
参数
Foo<T>
阅读来源
- 方法从 readSource 获取 T 的实例,并且不关心检索到的实际对象是否属于 T 的子类。
使用 <? super T>
如果……
- 一个方法有一个泛型类参数
Foo<T>
写目标
- 方法将 T 的实例放入 writeDest,并且不关心 writeDest 是否也包含作为 T 的子类的对象。
下面是一个具体示例的演练,说明了通配符背后的思想。假设您正在编写一个 processSquare 方法,该方法从列表中删除一个正方形,对其进行处理,并将结果存储在输出列表中。这是一个方法签名:
void processSquare(List<Square> iSqua, List<Square> oSqua)
{ Square s = iSqua.remove(0); s.doSquare(); oSqua.add(s); }
现在您创建一个扩展 Square 的 DoubleSquares 列表,并尝试处理它们:
List<DoubleSquare> dsqares = ...
List<Square> processed = new ArrayList<Square>;
processSquare(dsqares, processed); // compiler error! dsquares is not List<Square>
编译器因错误而失败,因为 dsquares List<DoubleSquare>
的类型与 processSquare 的第一个参数类型不匹配,List<Square>
.也许 DoubleSquare 是一个 Square,但是你需要告诉编译器一个 List<DoubleSquare>
是一个List<Square>
出于您的 processSquare 方法的目的。 使用<? extends Square>
通配符告诉编译器您的方法可以采用 Square 的任何子类的列表。
void processSquare(List<? extends Square> iSqua, List<Square> oSqua)
接下来,您将改进应用程序以处理圆形和方形。您希望将所有已处理的形状聚合到一个包含圆形和正方形的列表中,因此您将已处理列表的类型从 List<Square>
更改为到 List<Shape>
:
List<DoubleSquare> dsqares = ...
List<Circle> circles = ...
List<Shape> processed = new ArrayList<Square>;
processSquare(dsqares, processed); // compiler error! processed is not List<Square>
编译器因新错误而失败。现在处理列表的类型 List<Shape>
与 processSquare 的第二个参数不匹配,List<Square>
. 使用<? super Square>
通配符告诉编译器给定的参数可以是 Square 的任何父类(super class)的列表。
void processSquare(List<? extends Square> iSqua,
List<? super Square> oSqua)
这是示例的完整源代码。有时我发现从一个工作示例开始学习东西更容易,然后分解它以查看编译器的 react 。
package wild;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public abstract class Main {
// In processing the square,
// I'll take for input any type of List that can PRODUCE (read) squares.
// I'll take for output any type of List that can ACCEPT (write) squares.
static void processSquare(List<? extends Square> iSqua, List<? super Square> oSqua)
{ Square s = iSqua.remove(0); s.doSquare(); oSqua.add(s); }
static void processCircle(List<? extends Circle> iCirc, List<? super Circle> oCirc)
{ Circle c = iCirc.remove(0); c.doCircle(); oCirc.add(c); }
public static void main(String[] args) {
// Load some inputs
List<Circle> circles = makeList(new Circle());
List<DoubleSquare> dsqares = makeList(new DoubleSquare());
// Collated storage for completed shapes
List<Shape> processed = new ArrayList<Shape>();
// Process the shapes
processSquare(dsqares, processed);
processCircle(circles, processed);
// Do post-processing
for (Shape s : processed)
s.shapeDone();
}
static class Shape { void shapeDone() { System.out.println("Done with shape."); } }
static class Square extends Shape { void doSquare() { System.out.println("Square!"); } }
static class DoubleSquare extends Square {}
static class Circle extends Shape { void doCircle() { System.out.println("Circle!"); } }
static <T> List<T> makeList(T a) {
List<T> list = new LinkedList<T>(); list.add(a); return list;
}
}