【问题标题】:How to fill List<? extends Shape> with derived Circle and Rectangle objects?如何填写列表<?使用派生的 Circle 和 Rectangle 对象扩展 Shape>?
【发布时间】:2017-09-25 01:56:21
【问题描述】:

我有这些简单的类:

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) { ... }
}

这些类可以在画布上绘制:

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);
    }
}

一切都会编译。

如果我创建一个测试类并尝试创建一个这样的列表,问题就开始了:

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> 

然后遍历该列表以绘制形状。

提前致谢。

发布编辑:谢谢!那是一个相当大的突破。应该把功劳归功于 SpaceTrucker 和他关键的 PECS 链接,但那是不可能的。出于某种原因,我告诉自己我应该使用扩展,从那以后就再也没有质疑过这个假设。

【问题讨论】:

标签: java generics wildcard


【解决方案1】:

尝试指定List类型

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

【讨论】:

  • 斯帕西巴谢尔盖!
【解决方案2】:

由于圆形和矩形都是从 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()
    }

}

【讨论】:

  • 那个 instanceof 的用法很丑。我不会推荐它,因为人们可能会认为它在某种程度上是一种很好或可以接受的方式来编写你的逻辑。
  • 你能解释一下为什么它这么丑吗?我怎样才能更好地做到这一点?提前致谢
  • 它使代码依赖于实际的类,即使您很乐意处理Shape 接口。想象一下,您已经在多个地方编写了这样的代码,然后突然添加了一个 Triangle 形状。你必须去每个地方并添加instanceof 检查新课程。如果您打算将不同的类保留在同一个集合中,您应该能够通过接口来处理它们。如果不是,那么你有代码味道,也许它们一开始就不应该在同一个集合中。
【解决方案3】:

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

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

工作得很好。

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

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

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

阅读评论中关于 PECS 原则的链接,它应该会弄清楚一些事情。

【讨论】:

    猜你喜欢
    • 2014-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-07
    • 2023-01-09
    • 2013-10-10
    相关资源
    最近更新 更多