tags:

views:

293

answers:

5

Having the following simple class structure:

class A{}
class B extends A{}
class C extends B{}

I'm creating an ArrayList to keep objects of the earlier created classes:

 List<? extends A> list1 = new ArrayList<A>();
 List<? extends B> list2 = new ArrayList<B>();
 List<? extends C> list3 = new ArrayList<C>();

    List<? super A> list4 = new ArrayList<A>();
 List<? super B> list5 = new ArrayList<B>();
 List<? super C> list6 = new ArrayList<C>();

To each of those lists I'm trying to add 1 object of each earlier created class: A,B,C. The only possible combination is:

  • adding object of class A,B,C to list4

  • adding object of class B and C to list5

  • adding object of class C to list list6. The rest of the tries gives compiler errors, such us:

The method add(capture#1-of ? extends A) in the type List is not applicable for the arguments (A)

Why i cannot add any object of class A,B,C to list1/2/3? Why e.g. list4 accepts objects of classes A,B,C if they are supposed to be a super class of class A, as the list4 is defined?

+2  A: 

The type definition List<? extends A> is not usable for a mutable List - the explanation given in Java generics Java Generics Pdf is

The add() method takes arguments of type E, the element type of the collection. When the actual type parameter is ?, it stands for some unknown type. Any parameter we pass to add would have to be a subtype of this unknown type. Since we don’t know what type that is, we cannot pass anything in.

However, when the typedef is List<? super A> then the type parameter ? is implicitly typed.

Steve De Caux
you can use a quote block instead of a code block.
Martijn Courteaux
good point, looks much nicer now. Well spotted that man.
Steve De Caux
+1  A: 
List<? extends A> list1

It is a list, whose type element could be any unknown subclass of A. For example, it could be a D subclass. Therefore, you can't add anything to it, it could be wrong...

List<? super A> list4

It is a list, whose type element could be A, or a superclass of A (does not exist in that case, except Object). Therefore, you can add A objects to it, or any subclass of A such as B or C.

KLE
+3  A: 

"? extends A" means "some type derived from A (or A itself)". So for instance, a List<ByteArrayOutputStream> is compatible with List<? extends OutputStream> - but you shouldn't be able to add a FileOutputStream to such a list - it's meant to be a List<ByteArrayOutputStream>! All you know is that anything you fetch from the list will be an OutputStream of some kind.

"? super A" means "some type which is a superclass of A (or A itself)". So for instance, a List<OutputStream> is compatible with List<? super ByteArrayOutputStream>. You can definitely add a ByteArrayOutputStream to such a list - but if you fetch an item from the list, you can't really guarantee much about it.

See Angelika Langer's Generics FAQ for much more information.

Jon Skeet
Thanks, that's a good way of understanding! There's still one thing that's weird for me - I've found out that you can make a list:List<? extends B> list2_2 = new ArrayList<A>();instead of: List<? extends B> list2 = newArrayList<B>();and still add object of class B.
qbaquak
@qbaquak: That sounds very odd to me... that shouldn't work.
Jon Skeet
+1  A: 

It doesn't work that way.

You should use <? extends T> when you create function which argument is collection of unknown subtype of some type, and you want to fetch objects from that collection:

int sum(Collection<? extends Integer> collection) {
    for (Integer : collection) {
        // do sth
    }
    // ...
}

You cannot add new items to this collection, because you don't know which concrete type is this collection holding. All you know is that that type extends Integer.

You use <? super T> when you want to add new items of type T to collection and return that collection, but then you cannot guarantee what you can retrieve from it and you have to cast result of get() or check its type. You can safely add items of type T and subtypes, and retrieve items of type T.

MBO
A: 

List<? super B> allows you to use Lists of any supertype of B, i.e. list5 = new ArrayList<A>(); or list5 = new ArrayList<Object>();

You can safely add B (and subtypes) to every list that use supertypes of B, but you can not add any supertype of B. Imagine this:

public void myAdd(List<? super B> lst) {
  lst.add(new Object()) //this is a supertype of B (compile time error)
}
...
ArrayList<A> list = new ArrayList<A>();
myAdd(list); //tries to add Object to a list of type A
Zappi