List<? extends ListObject> list = new ArrayList<ListObject>();
list.remove(new ListObject()); // works
list.add(new ListObject()); // compile time error
Why do I get the compile time error? Specifically:
Test.java:14: cannot find symbol
symbol : method add(ListObject)
location: interface java.util.List<capture of ? extends ListObject>
list.add(new ListObject());
x
Also why does list.remove work then ?
It's very subtle, so pay attention and feel free to ask for
clarification if something doesn't make sense. It's because of the
wildcard in the type declaration.
Essentially, you've asked for a list whose element type is something
that is or extends ListObject. Note that this is subtly different from
a List whose element type is ListObject. Specifically, if you have two
variables declared as:
List<? extends ListObject> list1;
List<ListObject> list2;
Then:
class MyListObject extends ListObject { }
list1 = new ArrayList<MyListObject>(); // is legal
list2 = new ArrayList<MyListObject>(); // is NOT legal
But:
list2.add(new MyListObject()); // perfectly legal
So both references could point to lists that hold instances of
MyListObject. However, list1 contains the unique ability to hold
references to lists that are created to ONLY hold elements of
MyListObject. Do you see the difference?
The rest follows from there. 'list1' might be holding a reference to an
ArrayList<MyListObject>, in which case it would actually be illegal to
add a new object of class ListObject to that list. So it doesn't let
you do it. On the contrary, 'list2' knows that it is pointing to list
of ListObject -- not a subclass -- and so it can be proven that you can
safely add an object of class ListObject to the list.
As a further implication, you also could not add a new MyListObject via
list1, because it is just as clueless that the object it's pointing to
can contain instances of MyListObject. In fact, the only expression you
could legally add to list1 would be the literal 'null', which works
because it has magical type properties in which it adopts every possible
reference type at the same time. However, if you wanted to be able to
add MyListObject instances to 'list1', you could alter its type a bit to
look like this:
List<? extends ListObject super MyListObject> list1;
That's a mouthful, but it says that list1 (a) points to a List, and (b)
the element type of that list is assignable to ListObject, and (c) that
MyListObject is assignable to the element type of that list. That is
sufficient to prove to the compiler that it's safe to add MyListObject
instances to list1 (or instances of any subclass of MyListObject), so it
will let you do so.
You could try to pull the same trick with your original code, and you'd
end up with:
List<? extends ListObject super ListObject> list;
But the only type that is BOTH assignable to ListObject and assignable
from ListObject is -- you guessed it -- ListObject itself. So that long
declaration is semantically equivalent to:
List<ListObject> list;
So you may as well write the simpler code.
One important thing to bring out of this is a realization that you can
very easily say:
List<ListObject> list = new ArrayList<ListObject>();
list.add(new MyListObject());
So there's probably not a need to use the wildcard in the first place.
The wildcard is only necessary if you want to be able to point to lists
of types that are different from the base element type.
--
www.designacourse.com
The Easiest Way to Train Anyone... Anywhere.
Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation