0

I am a bit of confused about java generics

Here is the code

class Base{}

class Derived extends Base{}

WE can instantiate a list like this

List<? extends Base> list = new ArrayList<Base>();

Why cannot I add a a new item like this

list.add(new Base()); 

So user cannot use "add" method as far as a wildcard ? in the genetics type?

Thanks

icn
  • 17,126
  • 39
  • 105
  • 141

4 Answers4

7

PECS - producer extends, consumer super.

If you replace extends with super, you can add new Base().

List<? extends Base> means "a list that holds instances of any subclass of Base (or Base itself). But it cannot hold instances of two different subclasses.

If you want your list to hold Base and Derived, then use List<Base>. But note that it cannot be later cast to List<Derived>

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • 1
    That acronym by itself isn't very useful; see [this SO question](http://stackoverflow.com/questions/2723397/java-generics-what-is-pecs) for details. – Pops Apr 27 '11 at 21:49
  • @Lord Torgamus - yes, I'm already extending my answer to add proper explanation. – Bozho Apr 27 '11 at 21:49
  • But declaring the type as `List super Base>` makes no sense when you're assigning an `ArrayList` to it right there. Wildcards are for when you don't know the exact type parameter because it's being provided from elsewhere. – ColinD Apr 27 '11 at 21:50
  • @Bozho, yeah, I see it, your update and my comment were two seconds apart from each other. I still think it's a good link, though, so I'm going to leave it there. – Pops Apr 27 '11 at 21:52
  • @Lord Torgamus - yes, absolutely. +1 for the link – Bozho Apr 27 '11 at 21:52
  • thanks Bozho, I thought "When a method reads/iterates over elements in a collection then it is a consumer, when it add to a collections then it is a producer." but it seems from your answer it should be reverse "adding to a collection is consumer, and reading from a collection is a producer" – icn Apr 27 '11 at 21:59
  • I think "a list that holds instances of any subclass of `Base` (or `Base` itself)" describes `List` more than `List extends Base>`. `List extends Base>` is more like "a list that holds instances of one specific but unknown subtype of `Base` (which may be `Base` itself)". – ColinD Apr 27 '11 at 22:37
1

Just make it

List<Base> list = new ArrayList<Base>();

You shouldn't use wildcards when you know the actual type... just when you're being provided with something with an unknown type.

In such cases, ? extends Base means that the List is only allowed to contain some specific subtype of Base, but you don't know which subtype that is. Because of that, you can't add anything but null to the list.

ColinD
  • 108,630
  • 30
  • 201
  • 202
1

You can try reading ? as something:

List<? extends Base>

This is "List of something that extends Base". So it is clear that you cannot add a Base (just as you cannot add an Object to a List<String> even when String extends Object.

What you can do in your case is:

List<? super Base>

This is "List of something that is extended by Base". So you can add a Base there (just as you can add a String to a List<Object>, because Object is extended by String.

gpeche
  • 21,974
  • 5
  • 38
  • 51
0

I think this is a design of Java Generics. The wildcard ? extends Base is compiled to mean that the collection reference can point to a collection object that can hold any ( and all ) types that extend Base.You can write like this as well :

List<? extends Base> _listBaseSubtypes = new ArrayList<Derived>();

Now , with the above line , if you think about it , the below will be obviously an error :

        _listBaseSubtypes.add(new Base());

I think Java designers decided to allow the first line of code as valid. In order to avoid the runtime error that the second line of code can cause , it is caught at compile time.

Having said that , the question that comes to mind is : What type of object should be allowed to be added into the collection , given the fact that the actual collection object can be a collection of 'any' derived type ?

Because you can derive as many types as you want , and there cannot be found a single type that is assignment compatible with the type held in the actual collection object ( remember , the collection object could be declared to hold 'any' derived type ) , the simple answer to the question is : None. So , you cannot add any object into the collection through the add interface, because for any object that you may try passing into the add method , there will be complier objection raised on the reason that this type is not compatible with the type that the actual collection object holds.

Bhaskar
  • 7,443
  • 5
  • 39
  • 51