This question already has answers here:
Closed 7 years ago.
Possible Duplicate:
what is the difference between ‘super’ and ‘extends’ in Java Generics
A)
List<? super Shape> shapeSuper = new ArrayList<Shape>();
shapeSuper.add(new Square()); //extends from SHAP
shapeSuper.add(new DoubleSquare()); //extends from SQ
shapeSuper.add(new TripleSquare()); //extends from DS
shapeSuper.add(new Rectangle()); //extends from SHAP
shapeSuper.add(new Circle()); //extends from SHAP
for (Object object : shapeSuper) { ... }
Why must the iteration be of Objects when I can add only Shape and its
derivatives?
B)
List<? super Shape> shapeSuper = new ArrayList<Object>();
shapeSuper.add(new Object()); //compilation error
Why does the above line produce a compilation error?
For your example, you can use a plain List<Shape>
like Dan and Paul said; you don't need to use the wildcard question mark syntax such as List<? super Shape>
or List<? extends Shape>
). I think your underlying question may be, "when would I use one of the question mark style declarations?" (The Get and Put Principle that Julien cites is a great answer to this question, but I don't think it makes much sense unless you see it in the context of an example.) Here's my take at an expanded version of the Get and Put Principle for when to use wildcards.
Use <? extends T>
if...
- A method has a generic class
parameter
Foo<T>
readSource
- The method GETS instances of T from readSource, and doesn't care if the actual object retrieved belongs to a subclass of T.
Use <? super T>
if...
- A method has a generic class parameter
Foo<T>
writeDest
- The method PUTS instances of T into writeDest, and doesn't care if writeDest also contains objects that are subclasses of T.
Here's a walkthrough of a specific example that illustrates the thinking behind wildcards. Imagine you are writing a processSquare method that removes a square from a list, processes it, and stores the result in an output list. Here's a method signature:
void processSquare(List<Square> iSqua, List<Square> oSqua)
{ Square s = iSqua.remove(0); s.doSquare(); oSqua.add(s); }
Now you create a list of DoubleSquares, which extend Square, and try to process them:
List<DoubleSquare> dsqares = ...
List<Square> processed = new ArrayList<Square>;
processSquare(dsqares, processed); // compiler error! dsquares is not List<Square>
The compiler fails with an error because the type of dsquares List<DoubleSquare>
doesn't match the type of the first parameter to processSquare, List<Square>
. Perhaps a DoubleSquare is-a Square, but you need to tell the compiler that a List<DoubleSquare>
is-a List<Square>
for the purposes of your processSquare method. Use the <? extends Square>
wildcard to tell the compiler that your method can take a List of any subclass of Square.
void processSquare(List<? extends Square> iSqua, List<Square> oSqua)
Next you improve the application to process Circles as well as Squares. You want to aggregate all your processed shapes in a single list that includes both circles and squares, so you changed the type of the processed list from a List<Square>
to a List<Shape>
:
List<DoubleSquare> dsqares = ...
List<Circle> circles = ...
List<Shape> processed = new ArrayList<Square>;
processSquare(dsqares, processed); // compiler error! processed is not List<Square>
The compiler fails with a new error. Now the type of the processed list List<Shape>
doesn't match the 2nd parameter to processSquare, List<Square>
. Use the <? super Square>
wildcard to tell the compiler that a given parameter can be a List of any superclass of Square.
void processSquare(List<? extends Square> iSqua,
List<? super Square> oSqua)
Here's the full source code for the example. Sometimes I find it easier to learn stuff by starting with a working example and then breaking it to see how the compiler reacts.
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;
}
}
To expand on Paul's answer, by declaring shapeSuper as List<? super Shape>, you are saying that it can accept any object that is a super class of Shape. Object is a superclass of shape. This means that the common superclass of each of the list's elements is Object.
This is why you have to use the Object type in the for loop. As far as the compiler is concerned, the list might contain objects that are not Shapes.
The "Get and Put Principle" in section (2.4) is a real gem from Java Generics and Collections:
The Get and Put Principle: use an
extends wildcard when you only get
values out of a structure, use super
wildcard when you only put values into
a structure, and don't use a wildcard
when you both get and put.
Also, declaring a type as List<? super Shape> shapeSuper
is kind of bad form as it constrains its use. Generally, the only time I use wild cards is in method signatures:
public void foo(List<? super Shape> shapeSuper)
Try declaring shapeSuper as List<Shape>
instead. Then you can do
for (Shape shape : shapeSuper)
A)
Because super
indicates the lower bounding class of a generic element. So, List<? super Shape>
could represent List<Shape>
or List<Object>
.
B)
Because the compiler doesn't know what the actual type of List<? super Shape>
is.
Your adding an object with shapeSuper.add(new Object());
but the compiler only knows that the the generic type of List
is a super type of Shape
but doesn't know exactly witch one it is.
In your example, List<? super Shape>
could really be List<ShapeBase>
forcing the compiler to disallow the shapeSuper.add(new Object());
operation.
Remember, Generics are not covariant.
(Disclaimer: I've never used "super" as a generic wildcard qualifier, so take this with a grain of salt...)
For (A), actually you can't add Shape and its derivatives, you can only add Shape and its ancestors. I think maybe what you want is
List<? extends Shape> shapeSuper = new ArrayList<Shape>();
Specifying "extends" means Shape and anything derived from Shape. Specifying "super" means Shape and anything Shape descended from.
Not sure about (B), unless Object isn't implicit. What happens if you explicitly declare Shape as public class Shape extends Object
?
In reference to the above, I don't think this is correct:
by declaring shapeSuper as List<? super Shape> shapeSuper
, you are saying that it
can accept any object that is a super
class of Shape
That does seem the intuitive at first glance, but actually I don't think that's how it works. You cannot insert just any superclass of Shape into shapeSuper. superShape is actually a reference to a List which may be restricted to holding a particular (but unspecified) supertype of Shape.
Lets imagine Shape implements Viewable and Drawable. So in this case the superShape reference may actually point to a List<Viewable>
or a List<Drawable>
(or indeed a List<Object>
) - but we don't know which one. If it's actually a List<Viewable>
you should not be able to insert a Drawable
instance into it - and the compiler will prevent you from doing this.
The lower bound construct is still actually very useful in making generic classes more flexible. In the following example it allows us to pass into the addShapeToSet method a Set defined to containing any superclass of Shape - and we can still insert a Shape into it:
public void addShapeToSet(Set<? super Shape> set) {
set.add(new Shape());
}
public void testAddToSet() {
//these all work fine, because Shape implements all of these:
addShapeToSet(new HashSet<Viewable>());
addShapeToSet(new HashSet<Drawable>());
addShapeToSet(new HashSet<Shape>());
addShapeToSet(new HashSet<Object>());
}