How to fill List<? extends Shape> with derived Circle and Rectangle objects?
我有这些简单的课程:
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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 ) { ... }
} |
这些类可以绘制在画布上:
1 2 3 4 5
| public class Canvas {
public void draw (Shape s ) {
s. draw(this);
}
} |
我希望将所有圆形和矩形对象放在一个列表中,并在一个迭代语句中绘制所有对象,如下所示:
1 2 3 4 5
| public void drawAll (List <? extends Shape > shapes ) {
for (Shape s : shapes ) {
s. draw(this);
}
} |
一切都在编译。
如果我创建一个测试类并尝试创建这样的列表,问题就开始了:
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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 );
}
} |
如代码中所述,我不能向列表中添加圆形和矩形对象。
问题:我应该如何修改代码,以便构造
下一步迭代该列表以绘制形状。
事先谢谢。
文章编辑:谢谢!这是一个相当大的突破。本该把这归功于航天卡车司机和他关键的PECS链接,但这是不可能的。出于某种原因,我告诉自己应该使用扩展,从那以后再也不会质疑这个假设。
- 相关:stackoverflow.com/questions/2723397/&hellip;
- 它与官方教程基本相同:DOCS.Oracle、COM/JavaSe/Tube/Java/Guogys/Helip;
尝试指定List类型
1 2 3 4 5 6
| List <Shape > list = new ArrayList <Shape >();
Shape circle = new Circle ();
Rectangle rectangle = new Rectangle();
list. add(circle );
list. add(rectangle );
canvas. drawAll(list ); |
如果您只需要一个形状列表,就不需要任何通配符。
1 2 3
| List <Shape > foo = new ArrayList <>();
foo. add(new Circle ());
foo. add(new Rectangle()); |
工作很好。
如果你使用List extends Shape>,这意味着列表要么是List要么是List,并且你不能在其中添加任何内容,因为实际类型未知。但是,以下内容可以编译并正常工作:
1 2 3 4
| List<? extends Shape> foo = null;
foo = new ArrayList<Circle>();
foo = new ArrayList<Rectangle>();
foo = new ArrayList<Shape>(); |
你不知道foo的实际类型,但你知道你可以从那里得到一个Shape。
阅读评论中关于PECS原则的链接,它应该把事情弄清楚一点。
因为圆和矩形都是从形状延伸出来的,所以您可以做一个列表并添加这两个对象。
1 2 3 4 5
| List <Shape > list = new ArrayList <>();
Circle circle = new Circle ();
Rectangle rectangle = new Rectangle();
list. add(circle );
list. add(rectangle ); |
做这个,你可以使用你的形状的所有方法。如果要了解对象是圆形还是矩形以执行非形状方法,可以执行以下操作:
1 2 3 4 5 6 7 8 9
| 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()
}
} |
- 这种用法的例子真是难看。我不建议这样做,因为人们可能认为这是一种好的或可以接受的方法来编写逻辑代码。
- 你能解释一下为什么这么难看吗?我怎么能做得更好?提前谢谢
- 它使代码依赖于实际的类,即使您很乐意处理Shape接口。假设您在多个地方编写了这样的代码,然后突然添加了一个Triangle形状。你必须去每个地方,为新的班级添加一张instanceof支票。如果您打算在同一个集合中保留不同的类,那么应该能够通过接口处理它们。如果没有,那么你就有了一种代码味道,也许它们一开始就不应该在同一个集合中。