I have a class that has a constructor that takes
Collection<? extends Shape>
...
Then I try to create my Collection ...
private ArrayList<? extends Shape> shapes;
I think one thing that may be confusing is that just because there is a method parameter like that (Collection<? extends Shape>
), you don't need to create a variable like that. Think of the purpose of a variable/parameter that uses wildcard generics (? extends X or ? super Y) as a way to write some "generic" code that will work with any value from a family of similar types
rather than the usual "a value from a specific type".
--
The perfect example of this is the signature for Collection.copy():
public static <T> void copy(List<? super T> dest,List<? extends T> src)
This method allows me to pass an instance of List<Integer>
and have its values copied into an instance of List<Number>
. It also allows me to copy List<Integer>
into List<Integer>
. It lets me use the same code with different types ("family of types"). I can test it with using wildcard generic variables at all:
List<Integer> src = new ArrayList<>();
src.add(1);
List<Number> dest = new ArrayList<>();
Collection.copy(src, dest);
assert(src.get(0), dest.get(0));
--
The big tradeoff with using a variable or parameter like List<? super T>
or List<? extends T>
is that because its not a variable for a specific type, there are limitations on what you can assume about the instance it points to and therefore you are limited in what you can do with it.
PECS
is a tiny bit of an oversimplification, but it makes a great mnemonic: PECS
= "producer extends, consumer super" means that a variable (or parameter) for a generic wildcard type that uses extends
can only be used to "produce" values, i.e. you can safely only read from it, you can't add to it. Similarly, a variable for a generic wildcard type that uses super
can only be used to "consume" values - you can safely only feed it values (add), but there are limitations on what you can assume about the values that could be "read" from it.
So, for your example, I would avoid using a variable with a wildcard generic at all, just pass in different List<> types to the constructor. If you must use a variable with a wildcard generic, that use a variable to the specific type to fill it because the variable with the wildcard generic that uses extends
can't be used to fill it:
private ArrayList<Shape> shapesSpecific;
private ArrayList<? extends Shape> shapesExtends;
shapesExtends = shapesSpecific = new ArrayList<Shape>();
shapesSpecific.add(new TestShape()); // fine
//shapesExtends.add(new TestShape()); // error - but the line above added a value already
// Another option - create it already filled:
// shapeExtends = Arrays.asList(new TestShape());
new Foo(shapesSpecific); // fine
new Foo(shapesExtends); // fine
--
You might find this question/answer useful:
How can I add to List<? extends Number> data structures? - here's an example from it:
The wildcard declaration of List<? extends Number> foo3
means that
the variable foo3
can hold any value from a family of types (rather
than any value of a specific type). It means that any of these are
legal assignments:
List<? extends Number> foo3 = new ArrayList<Number>; // Number "extends" Number
List<? extends Number> foo3 = new ArrayList<Integer>; // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>; // Double extends Number
So, given this, what type of object could you add to List foo3
that
would be legal after any of the above possible ArrayList
assignments:
- You can't add an
Integer
because foo3
could be pointing at a List<Double>
.
- You can't add a
Double
because foo3
could be pointing at a List<Integer>
.
- You can't add a
Number
because foo3
could be pointing at a List<Integer>
.
You can't add any object to List<? extends T>
because you can't guarantee what kind of List
it is really pointing to, so you can't
guarantee that the object is allowed in that List
. The only
"guarantee" is that you can only read from it and you'll get a T
or
subclass of T
.
This is a similar question/answer:
Difference between <? super T> and <? extends T> in Java