2

I have something like:

List<? extends BaseClass> a = getMyList();

Any of the following two instructions are invalid, and the compiler says that it requires a "? extends BaseClass" as an argument.

a.add(new BaseClass());
a.add(new SubClass());

I guess the problem is that

How can this be solved?

CCC
  • 2,642
  • 7
  • 40
  • 62
  • which line does compiler point to saying that it requires a `? extends BaseClass`? – Vikdor Sep 21 '12 at 19:14
  • 2
    See the accepted answer here: http://stackoverflow.com/questions/11781429/adding-elements-to-list-extends-superclass-clarification-needed – Natix Sep 21 '12 at 19:20

3 Answers3

3

It should be List<? super BaseClass> a to add stuffs to it. You can only get stuffs from List<? extends BaseClass> a.

Take look at this What is PECS (Producer Extends Consumer Super)?.

Community
  • 1
  • 1
Bhesh Gurung
  • 50,430
  • 22
  • 93
  • 142
2

The problem is that the compiler has no way of determining the actual type of the list.

It could be that the type is SubClass, in which case you would not be allowed to add a BaseClass.
It could even be SubSubClass, meaning you couldn't add either BaseClass or SubClass.

However, this will compile:

List<BaseClass> a = getMyList();
a.add(new BaseClass());
a.add(new SubClass());
Bohemian
  • 412,405
  • 93
  • 575
  • 722
1

Usually Java tutorials say you can't add anything to a List<? extends Something> because the compiler can't know the effective type in the list.

I found this explanation non intuitive, and also not strictly true, because it pretends the compiler is a smart being, which understands that a List is an ordered container of elements and prevents you from doing potentially unsafe things. In reality, a compiler is just a program, and it simply obeys some rules.

So, it's best to do some role playing, and thinking like a compiler. You have

interface List<E> {
  void add(E element);
}

Note that <E> doesn't provide any extra semantic to the compiler, in the sense that it doesn't know that you are defining a container type. You could define a Asdf<Q>, and for the compiler it doesn't matter, or an Elephant<W> and still the same rules apply (and clearly Elephant is not a container type, ie you don't add anything to an elephant - I hope...)

So you declare a reference of type List<? extends Shape>. The compiler understands something similar

abstract class Unknown extends Shape {}

class ListOfUnknown {
  void add(Unknown element) {}
  Unknown get(int index) {}
}

Can you see why you can't add a Rectangle to such a list? According to normal Java rules, you can supply a Rectangle to a method such as add(Shape) because their interfaces are assured to be compatible by the subtyping relation.

To be allowed to invoke add(Unknown) with an argument of type Rectangle, Rectangle should be a child of Unknown, but Rectangle only extends Shape, so there is nothing really special to generics here, it's the normal Java rule for type compatibility. To be able to call add(Unknown) you'd need a reference to an object of type Unknown or equivalent (some class which extends Unknown), but as you can see there is no such type defined anywhere, so this eventually prohibits to add() anything to this list.

When studying generics, always ask yourself "Why?" and never stop at the List examples because, even if generics were primary added for collections, they are a language feature, so you must understand the semantics they carry on, and not concentrating on the specific implementation of a container type.

Raffaele
  • 20,627
  • 6
  • 47
  • 86