对于你的例子,你可以像丹和保罗说的那样使用一个平原;您不需要使用通配符问号语法,例如 或 )。我想你的潜在问题可能是,“我什么时候会使用问号样式声明之一?(Julien引用的Get and Put原则是这个问题的一个很好的答案,但我认为除非你在例子的上下文中看到它,否则它没有多大意义。以下是我对 Get and Put 原则的扩展版本的看法,了解何时使用通配符。List<Shape>
List<? super Shape>
List<? extends Shape>
在以下情况下使用...<? extends T>
- 方法具有泛型类参数 readSource
Foo<T>
- 方法从 readSource 获取 T 的实例,并且不关心检索到的实际对象是否属于 T 的子类。
在以下情况下使用...<? super T>
- 方法具有泛型类参数 writeDest
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 的类型与处理 Square 的第一个参数的类型不匹配。也许 DoubleSquare 是 Square,但你需要告诉编译器,对于你的 processSquare 方法来说,a is-a。使用<? 扩展 Square>
通配符告诉编译器您的方法可以获取 Square 的任何子类的列表。List<DoubleSquare>
List<Square>
List<DoubleSquare>
List<Square>
void processSquare(List<? extends Square> iSqua, List<Square> oSqua)
接下来,您将改进应用程序以处理圆和正方形。您希望将所有已处理的形状聚合到一个包含圆形和正方形的列表中,因此您将已处理列表的类型从 a 更改为 :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>
编译器失败并显示新错误。现在,已处理列表的类型与处理平方的第二个参数不匹配。使用<? super Square>
通配符告诉编译器给定的参数可以是 Square 的任何超类的列表。List<Shape>
List<Square>
void processSquare(List<? extends Square> iSqua,
List<? super Square> oSqua)
下面是该示例的完整源代码。有时我发现从一个工作示例开始,然后打破它来了解编译器的反应,从而更容易学习东西。
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;
}
}