如何填写列表<?使用派生的 Circle 和 Rectangle 对象扩展 Shape>?

How to fill List<? extends Shape> with derived Circle and Rectangle objects?

我有这些简单的 classes:

public abstract class Shape {
    public abstract void draw(Canvas c);
}

public class Circle extends Shape {
    private int x, y, radius;
    public void draw(Canvas c) { ... }
}

public class Rectangle extends Shape {
    private int x, y, width, height;
    public void draw(Canvas c) { ... }
}

这些 classes 可以画在 canvas:

public class Canvas {
    public void draw(Shape s) {
        s.draw(this);
    }
}

我想将所有 Circle 和 Rectangle 对象放在一个 List 中,并在一个迭代语句中绘制所有对象,如下所示:

public void drawAll(List<? extends Shape> shapes) {
    for (Shape s : shapes) {
        s.draw(this);
    }
}

编译一切。

如果我进行测试 class 并尝试创建如下列表,问题就开始了:

public class Main {

    public static void main(String[] args) {

        Canvas canvas = new Canvas();

        List<? extends Shape> list = new ArrayList<>();
        Circle circle = new Circle();
        Rectangle rectangle = new Rectangle();
        list.add(circle); // The method add(capture#1-of ? extends Shape) in the type List<capture#1-of ? extends Shape> is not applicable for the arguments (Circle)
        canvas.drawAll(list);
    }
}

如代码中所述,我无法将 Circle 和 Rectangle 对象添加到列表中。

问题:我应该如何修改代码,以便我可以构造一个

List<? extends Shape> 

接下来遍历该列表以绘制形状。

提前致谢。

Post 编辑:谢谢!那是一个很大的突破。应该将功劳归功于 SpaceTrucker 和他至关重要的 PECS link,但这是不可能的。出于某种原因,我告诉自己我应该使用 extends 并且从那以后就再也没有质疑过这个假设。

尝试指定List类型

List<Shape> list = new ArrayList<Shape>();
Shape circle = new Circle();
Rectangle rectangle = new Rectangle();
list.add(circle);
list.add(rectangle);
canvas.drawAll(list);

由于圆形和矩形都是从 Shape 扩展而来的,因此您可以创建一个 List 并添加这两个对象。

List<Shape> list = new ArrayList<>();
Circle circle = new Circle();
Rectangle rectangle = new Rectangle();
list.add(circle);
list.add(rectangle);

这样,您就可以使用 Shape 的所有方法。如果你想知道一个对象是圆形还是矩形以便执行非形状方法,你可以这样做:

for(Shape shape : list){
    if(shape instanceof Circle){
        //do stuff casting object : ((Circle)shape).method()
    }
    else if(shape instanceof Rectangle){
        //do stuff casting object : ((Rectangle)shape).method()
    }

}

如果您只需要一个形状列表,则不需要任何通配符。

List<Shape> foo = new ArrayList<>();
foo.add(new Circle());
foo.add(new Rectangle());

工作正常。

如果您要使用 List<? extends Shape>,则意味着该列表是 List<Circle>List<Rectangle>,并且您不能在其中添加任何内容,因为实际类型不是'不知道。但是,以下内容可以编译并正常工作:

List<? extends Shape> foo = null;
foo = new ArrayList<Circle>();
foo = new ArrayList<Rectangle>();
foo = new ArrayList<Shape>();

您不知道 foo 的实际类型,但您知道可以从那里得到 Shape

阅读评论中的 link PECS 原则,它应该会清楚一点。