1

This could be a very stupid question, however I don't understand why the compiler complains and compiles.

I have two very simple classes:

class A {
}

class B extends A {
}

Now the codes:

//block1
List<A> list = new ArrayList<>();
list.add(new A()); //ok
list.add(new B()); //ok 

//block2
List<? extends A> extendList= new ArrayList<>();
extendList.add(new A()); //not ok, why?
extendList.add(new B()); //not ok, why?

//block3
List<? super A> superList = new ArrayList<>();
superList.add(new A()); //ok
superList.add(new B()); //ok. why?

The block1 I know why it worked.

The block2, I have <? extends A>, as I understood, the list is gonna accept objects with type A or subType of A, for example B. Why both add() lines failed? with error:

Error: no suitable method found for add(A)
method java.util.Collection.add(capture#1 of ? extends A) is not applicable
  (argument mismatch; A cannot be converted to capture#1 of ? extends A)
method java.util.List.add(capture#1 of ? extends A) is not applicable
  (argument mismatch; A cannot be converted to capture#1 of ? extends A)

The block3, I have <? super A>, as I understood, the list is gonna accept objects with type A or superType of A, B is a subType of A, why add(new B()) compiles?

I think I could have misunderstanding of the super and extends keywords, I did some google, however my doubt is still there.

A sentence from oracle generic tutorial: (https://docs.oracle.com/javase/tutorial/java/generics/upperBounded.html)

The term List<Number> is more restrictive than List<? extends Number>
because the former matches a list of type Number only, whereas the 
latter matches a list of type Number or any of its subclasses.
Kent
  • 173,042
  • 30
  • 210
  • 270

3 Answers3

4

block2: "as I understood, the list is gonna accept objects with type A or subType of A, for example B" - no! Think about the possible values of ?, e.g. it could be a class C extends A { } and that would mean that neither A nor B match the constraint. You cannot add A or B into a list with generic type C.

block3: once again think about the possible values of ?: right now it can be A or any of its super classes, so A or Object (or anything in between). Since B is a subtype of A it is of course a sub type of all the super classes of A as well. Every List that accepts an A will accept a B as well.

luk2302
  • 46,204
  • 19
  • 86
  • 119
1

Here's a scenario to make it clear. Consider a third class:

class C extends A {
}

List<C> cList = new ArrayList<>();
List<? extends A> extendList = cList; //this is valid. Right? Yes

With that, the reason for the failure becomes clear. If extendList.add(new A()) were allowed, the following would also have to be legal:

extendList.add(new B());

But then we would be adding an incompatible type (B) to the list (of C)

The reason is the boundary: <? super A> is guaranteed to be compatible with any subtype of A. However, <? extends B> allows subtypes of A that could be incompatible with one another.

ernest_k
  • 39,584
  • 5
  • 45
  • 86
0

List<? extends A> extendList= new ArrayList<>(); extendList.add(new A()); //not ok, why? extendList.add(new B()); //not ok, why?

Imagine that your A is Animal. Now you have a List<? extends Animal>, so you have a list of something that is an animal, it can be a list of Dog, Cat, or even Animal, but you don't know which one. You are trying to insert something into it (impossible by the way), and you can't do it, because like I said, you don't know what is in the list. It could be List<Cat> and you are trying to insert a Dog into it. You can't even insert Animal - because Animal could be a Dog, who knows.

You can get from that list tho, because you know that whatever you pick from that list can be assigned to Animal, so no problems there. Notice that in block 3 you can't get anything from the list, but you can insert - it's the other way around.

You can insert into List<? super Animal>, because you know that whatever is in there is an Animal or higher. So you can insert Dog, Cat or Animal, because they are all animals. Notice that you can't get anything from that list. It's a list of things that are supertype of Animal, list of LivingBeing for example (Animal, Human etc.). What would you assign it to? Human human = list.get(0) - what if that particular object is not a Human but an Animal? LivingBeing livingBeing = list.get(0) - what if it's not a list of livingBeings, but something higher or simply different than that, but still a supertype of Animal?

Have a look at Effective Java 3rd edition and Koltin in Action - yes, Kotlin, it has a slightly different approach to this which can help you understand it more. Kotlin language has put all of that into clean rules.

Shadov
  • 5,061
  • 2
  • 16
  • 27