问题
I have these simple 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) { ... }
}
These classes can be drawn on a canvas:
public class Canvas {
public void draw(Shape s) {
s.draw(this);
}
}
I would like to put all Circle and Rectangle objects in a single List and draw all of them in a single iteration statement, like this:
public void drawAll(List<? extends Shape> shapes) {
for (Shape s : shapes) {
s.draw(this);
}
}
Everything compiles.
The problem begins if I make a test class and try to create a list like 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);
}
}
As is documented in the code, I cannot add Circle and Rectangle objects to the List.
Question: how should I modify the code such, that I can construct a
List<? extends Shape>
and next iterate over that List to draw the shapes.
thanks in advance.
Post Edit: Thanks! That was quite a breakthrough. Should have given the credits to SpaceTrucker and his crucial PECS link, but that wasn't possible. For some reason I had told myself that I should use extends and never questioned that assumption since.
回答1:
Try to specify List
type
List<Shape> list = new ArrayList<Shape>();
Shape circle = new Circle();
Rectangle rectangle = new Rectangle();
list.add(circle);
list.add(rectangle);
canvas.drawAll(list);
回答2:
Since both circle and rectangle extend from Shape, you can do a List and add both objects.
List<Shape> list = new ArrayList<>();
Circle circle = new Circle();
Rectangle rectangle = new Rectangle();
list.add(circle);
list.add(rectangle);
Doing this, you can use all your Shape's methods. If you want to know if an object is Circle or Rectangle in order to do a non-shape method, you can do:
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()
}
}
回答3:
If you just want a list of shapes, you don't need any wildcards.
List<Shape> foo = new ArrayList<>();
foo.add(new Circle());
foo.add(new Rectangle());
works just fine.
If you were to use List<? extends Shape>
it would mean that the list is either a List<Circle>
or List<Rectangle>
, and you can't add anything there since the actual type isn't known. However the following would compile and work just fine:
List<? extends Shape> foo = null;
foo = new ArrayList<Circle>();
foo = new ArrayList<Rectangle>();
foo = new ArrayList<Shape>();
you don't know the actual type of foo
, but you know that you can get a Shape
out from there.
Read the link in the comment for the PECS principle and it should clear things up a bit.
来源:https://stackoverflow.com/questions/43654936/how-to-fill-list-extends-shape-with-derived-circle-and-rectangle-objects