2
votes

In java generic I understood what are the meanign of wild card, super and extends, but didn't get why does not allow me to add anything, and why allows me to add upto SomeType in hierarchy, but not above in the hierarchy?

class Animal {}
class Cat extends Animal{}

following method can take list of Animal or sub of Animal i.e Cat, but nothing else and I am not allowed to add anything, if try to add, compiler stops me why ?

void addAminal(List<? extends Aminal> aList){
       aList.add(new Cat()); // compiler error
       aList.add(new Animal()); // compiler error
}

Now following method can take any list of Animal or any super type of Animal, but no sub type of Animal, and I can add objects upto Animal or lower in hierarchy, so when I try to add Object, compiler complains why ?

void addAnimal(List<? super Animal> aList){
     aList.add(new Animal()); // no error
     aList.add(new Cat());     // no error
     aList.add(new Object()); // compiler error why ?
}

Thanks Arya

4
I advise you to take a look at Java Generics Guide and other questions regarding covariance and contravariance.dm3
I have to admit it's a dry read, but the PDF on Java Generics that @dm3 linked has valuable information. And it's geared toward people who understand Java fundamentals but don't quite grasp generics. In other words, judging by this question, it was written for you, Bharat! :-)corsiKa

4 Answers

4
votes

Suppose you defined a new class:

class Tabby extends Cat {}

And then you did the following:

List<Tabby> aList = new ArrayList<Tabby>();
addAnimal(aList);

There's no surprise that this list should not have an Animal or even a Cat that isn't a Tabby, yet if the compiler didn't flag the error, that's what you would have.

The reason is that hou've specified addAnimal to take a list of something that extends Animal, but that something could be highly restrictive. This, however, would compile:

void addAnimal(List<Animal> aList){
    aList.add(new Cat()); // OK
    aList.add(new Animal()); // OK
}

The use of super also would work, because an instance of either Cat or Animal is an instance of any superclass of Animal.

1
votes

List<? extends Animal> means List<X> where X is an unknown subtype of Animal.

Therefore it has methods

void add(X item);
X get(int i);

You can't call add(cat), because we don't know if Cat is a subtype of X. Since X is unknown, the only value that we knows is a subtype of X is null, so you can add(null) but nothing else.

We can do Animal a = list.get(i), because the method returns X and X is a subtype of Animal. So we can call get(i) and treat the return value as an Animal.

Conversely, List<? super Animal> means List<Y> where Y is an unknown super type of Animal. Now we can call add(cat), because Cat is a subtype of Animal, Animal is a subtype of Y, therefore Cat is a subtype of Y, and add(Y) accepts a Cat. On the other hand, Animal a = list.get(0) won't work now, because Animal is not a super type of the return type Y; the only known super type of Y is Object, so all we can do is Object o = list.get(0).

0
votes

The generics only allow you to add an object of the type (or a subtype) of the type given as type parameter. If you put <? extends Animal> it means the list has SOME type that is a subclass of animal. Since you are trying to add a Cat to it, you have to be sure that it is indeed a list of Cats, and not of Dogs.
Basically, when you use a wildcard you will not be able to add new items to such a list (note: I don't have full knowledge and this might not be fully correct, but it seems like this. Forgive me if I'm wrong)

If you want to be able to add any Animal to the list, just use List<Animal>.

0
votes

Well, When you say ArrayList< ? extends Animal > you are specifying that this list will contain any specific type (as ? refers to a specific/definite type) that is of type Animal or anything inherited from Animal but something definite. So ultimately, as the generics are implemented with Eraser concept (which replaces every generic type in the program by a non-generic upper bound), this list is supposed to contain a specific type but due to ( < ? extends Animal >) you don't know which specific type is that. And hence you are not allowed to add even though types are inherited from Animal.

But when you say ArrayList< ? super Animal >, it means the arraylist contains specific type derived from Animal that is, objects whose base or super type is Animal. Hence it is safe to pass a Animal or anything derived from Animal into this list. The list is treated that way and it is allowed to add objects as mentioned. And hence it works.

Hope it helps!