import java.util.List;
import java.util.ArrayList;
interface Canine {}
class Dog implements Canine {}
public class Collie extends Dog {
public static void main(String[] args){
List<Dog> d = new ArrayList<Dog>();
List<Collie> c = new ArrayList<Collie>();
d.add(new Collie());
c.add(new Collie());
do1(d); do1(c);
do2(d); do2(c);
}
static void do1(List<? extends Dog> d2){
d2.add(new Collie());
System.out.print(d2.size());
}
static void do2(List<? super Collie> c2){
c2.add(new Collie());
System.out.print(c2.size());
}
}
The answer for this question tell that when a method takes a wildcard generic typ, the collection can be accessed or modified, but not both. (Kathy and Bert)
What does it mean 'when a method takes a wildcard generic typ, the collection can be accessed or modified, but not both' ?
As far as I know,
The method do1 has List<? extends Dog> d2
so d2 only can be accessed but not modified.
The method d2 has List<? super Collie> c2
so c2 can be accessed and modified and there is no compilation error.
Generic guidelines
The answer for this question tell that when a method takes a wildcard generic typ, the collection can be accessed or modified, but not both. (Kathy and Bert)
That's a fair first approximation, but not quite correct. More correct would be:
You can only add null to a Collection<? extends Dog>
because its add method takes an argument of ? extends Dog
. Whenever you invoke a method, you must pass parameters that are of a subtype of the declared parameter type; but for the parameter type ? extends Dog
, the compiler can only be sure that the argument is of compatible type if the expression is null
. However, you can of course modify the collection by calling clear()
or remove(Object)
.
On the other hand, if you read from a Collection<? super Dog>
, its iterator has return type ? super Dog
. That is, it will return objects that are a subtype of some unknown supertype of Dog
. But differently, the Collection could be a Collection<Object>
containing only instances of String
. Therefore
for (Dog d : collection) { ... } // does not compile
so the only thing we know is that instances of Object are returned, i.e. the only type-correct way of iterating such a Collection is
for (Object o : collection) { ... }
but it is possible to read from a collection, you just don't know what types of objects you will get.
We can easily generalize that observation to: Given
class G<T> { ... }
and
G<? extends Something> g;
we can only pass null to method parameters with declared type T
, but we can invoke methods with return type T
, and assign the result a variable of type Something
.
On the other hand, for
G<? super Something> g;
we can pass any expression of type Something
to method parameters with declared type T
, and we can invoke methods with return type T
, but only assign the result to a variable of type Object
.
To summarize, the restrictions on the use of wildcard types only depend on the form of the method declarations, not on what the methods do.
You cannot add a Cat
to a List<? extends Animal>
because you don't know what kind of list that is. That could be a List<Dog>
also. So you don't want to throw your Cat
into a Black Hole
. That is why modification
of List
declared that way is not allowed.
Similarly when you fetch something out of a List<? super Animal>
, you don't know what you will get out of it. You can even get an Object
, or an Animal
. But, you can add an Animal
safely in this List
.
I pasted your code into my IDE. The following error was signalled inside do1
:
The method add(capture#1-of ? extends Dog) in the type List is not applicable for the arguments (Collie)
This is, of course, as expected.
You simply cannot add a Collie
to a List<? extends Dog>
because this reference may hold for example a List<Spaniel>
.
I pasted your code into IDEONE http://ideone.com/msMcQ. It did not compile for me - which is what I expected. Are you sure you did not have any compilation errors?