3

I can work with basic generic expression but the wildcard-constraints just mess with my mind.

Info: Student extends Person and Person extends Animal

 List<? super Animal> aList= new ArrayList<>();

       // does not compile as expected, 
       // since the list is restricted to something that has Animal as superclass.
       // aList.add(new Object()); 

    aList.add(new Animal()); // I can add animal
    aList.add(new Person()); // I can add Person
    aList.add(new Student()); // I can add Student

    Animal a = new Animal();
    Animal b = new Animal();
    Person p = new Person();
    Student s = new Student();
    // test
    a = b; //I can assign an animal to another animal
    a = p; // I can assign a person to an animal
    a = s; // I can assign a student to an animal

    Animal animal = aList.get(0); // DOES NOT COMPILE, WHY ?

QUESTION: I don't understand why the last assignment does not work. The examples above show, that anything in that list is definitely an animal, yet I can't get an animal from that list.

MORE SPECIFIC: When I know that I can add only types which have Animal as superclass, why can't I expect to remove objects from type Animal ?

FACT: I can ONLY add objects which extend Animal! I just tried to add a car object and it does not work !

I am starting to doubt my sanity, I hope you can help me. Thank You

Additionally:

Object animal = aList.get(0); // works

Why does this statement work even though I know that I can't add an Object-Type ?

SOLUTION: (Based on the accepted answer)

I misunderstood the meaning of <? super Animal>

What I thought it means: Any class which has Animal as superclass.

What it (apparently) means: Any Class which is superclass of Animal.

Therefore a List might also contain objects of type Object which is why Animal animal = aList.get(0); fails.

Cheers

SklogW
  • 839
  • 9
  • 22
  • I think you just want : List in your aList declaration – Amir Afghani Jun 24 '15 at 16:58
  • 1
    What about `Animal animal = aList.get(1)` (with your example)? If you can answer that question, then you have the reason why it is not allowed. – Seelenvirtuose Jun 24 '15 at 16:58
  • I wan't to understand wildcard-restraints. Thank You – SklogW Jun 24 '15 at 16:59
  • 5
    Are you sure that you are correctly differentiating `super` and `extends`? http://stackoverflow.com/questions/4343202/difference-between-super-t-and-extends-t-in-java – user3707125 Jun 24 '15 at 16:59
  • 1
    `List super Animal>` does *not* mean: "something that has Animal as superclass", it means: some unknown type that is a superclass of `Animal`. So, it's the other way around than what you say in the comment. – Jesper Jun 24 '15 at 17:01
  • Yes, but an object from a Class which does not extend animal can NOT be added to the list, so why can't i expect to at least get an Animal in return ? – SklogW Jun 24 '15 at 17:06
  • @Seelenvirtuose but a person is an animal so the assignment Animal animal = new Person() is allowed as stated in the tests. – SklogW Jun 24 '15 at 17:16
  • My compiler disagrees. – SklogW Jun 24 '15 at 17:19
  • 1
    @Seelenvirtuose: Persons are humans,Humans are mammals, mammals are animals. or: (persons -> humans) && (humans -> mammals) && (mammals -> animals) <=> persons -> animals. (of course with the exceptions of those carved from a rip and made of clay.) – vikingosegundo Jun 24 '15 at 17:36
  • @vikingosegundo Now even I got confused. – Seelenvirtuose Jun 24 '15 at 17:40

3 Answers3

16

The other answers here are right, but not stated clearly enough, which is where the confusion is coming from.

A List<? extends Animal> does not mean "A list of things that all extend Animal." It means "A list of some type T, which I won't tell you what it is, but I know that T extends Animal." (In type theory, these are called existential types -- there exists a type T for which our List is a List<T>, but we don't necessarily know what T is.)

The difference is important. If you have "a list of things that all extend Animal", then it would be safe to add a Dog to the list -- but this is not what wildcards express. A List<? extends Animal> means "List of something, where that something extends Animal". It might be a List<Animal>, or a List<Dog>, or a List<Cat> -- we don't know. So we have no reason to think that its safe to add a Dog to that list -- because maybe my List is a List<Cat>.

In your example, you have a List<? super Animal>. This means, a list of some type T, where T is one of the supertypes of Animal. It might be a List<Object>, or a List<Animal>, or if Animal has a supertype HasLegs, it might be a List<HasLegs>. Here, it's safe to put a Dog into this list -- because whatever its a List of, Dog is definitely one of those (Animal, Object, etc), but when you take something out of the list, you have no idea whether its a Dog, an Animal, or just an Object.

Brian Goetz
  • 90,105
  • 23
  • 150
  • 161
  • I still don't understand why I can't add something of type Object to the list but on the other hand I can get it from the list ? I guess I am too stupid for this. It is like a bag of different colored marbles, I know i can't get the color right but I know that everything in there is at least spheric. – SklogW Jun 24 '15 at 17:25
  • 4
    When you have a `List extends Animal>` (could be List, List, List), you can _take animals out_, but you can't _put animals in_. When you have a `List super Animal>` (could be List, List), you can put animals in, but you can't take them out. Only when you have an invariant list -- List -- can you both take animals out and put them in. – Brian Goetz Jun 24 '15 at 17:27
  • 2
    @SklogW Probably because you're still interpreting the wildcard wrong. `List extends Animal>` does not mean "A list of things, all of which extend Animal." It is this common intuition that leads people into confusion; once you kick this intuition and realize what wildcards actually mean, it makes much more sense.... – Brian Goetz Jun 24 '15 at 18:47
  • I think I got it. Thank you for your effort. – SklogW Jun 24 '15 at 19:23
10

List<? super Animal> aList is reference which can be used to handle List<Animal> , it can also be any superclass of Animal such as List<Object>.

In other words List<? super Animal> aList
- is not reference to some list which can store any supertype of Animal.
+ it is reference to list of some specific type which is supertype of Animal (including Animal itself) but you don't know which type it is exactly.

So it could be

List<Object> someList = new ArrayList<>();
someList.add(new Car());//OK since Car is a Object
List<? super Animal> aList = someList;// OK since Object is supertype of Animal

Because of that, code

Animal a = aList.get(0);//and where did that Car come from?

is not safe. Only safe type to store the result of get(0) is Object so you either need

Object o = aList.get(0);

or if you want to make sure that get will return Animal, change the type of your aList reference to List<Animal> or even List<? extends Animal>.

Pshemo
  • 122,468
  • 25
  • 185
  • 269
  • I just tried it. If I create a class Car and try to add a Car to the list then it does not work. It is not compatible to super Animal> – SklogW Jun 24 '15 at 17:04
  • @SklogW I added example showing how you can end up with wanting to place Car in Animal reference. – Pshemo Jun 24 '15 at 17:08
  • So this is needed simply because Java allows it to assign a restrained list to an obejct list ? It makes sense now why I might expect a Car-object but that java allows this is ridiculous. – SklogW Jun 24 '15 at 17:14
  • 2
    What do you mean? Letting `List super Animal>` be reference to specific lists like `List` or `List` or even `List` is entire purpose of ` super Animal>` and because of that it would be unreasonable to simply assume that `List` or `List` will contain only `Animals`. – Pshemo Jun 24 '15 at 17:19
2

List<? super Animal> means a list of any superclass of Animal. If Animal is a subclass of LivingThing, then List<? super Animal> can be a list of Animals, a list of LivingThings, or a list of Object. In the last two cases, the assignment Animal x = aList.get(0) would of course be illegal, because you cannot assign a LivingThing to a reference of type Animal.

Consider the following method (it will not compile):

void f(List<? super Animal> list) {
    Animal a = list.get(0); // line 1
    list.add(new Object()); // line 2
}

The method call

List<Object> list = new ArrayList<>();
list.add(new Object());
f(list);

is correct, because Object is a superclass of Animal. However, if the method f would compile, you would try to assign in Line 1 an Object to an Animal reference, so it would not be type safe.

On the other, hand consider the method call:

f(new List<Animal>());

If the method f would compile, we would now be adding an Object instance to a list which should only contain Animals.

That's why both Line 1 and Line 2 are not allowed. If you have a class

public class A<T> {
    public put(T in) { } 
    public T get() { } 
}

then for the type A<? super Animal> the method get will return a Object (not an Animal), while the method put expects a parameter of type Animal.

Hoopje
  • 12,677
  • 8
  • 34
  • 50
  • Then why can't I add an object of a superclass of animal ? I can only add objects which have Animal as superclass. – SklogW Jun 24 '15 at 17:09
  • 1
    @SklogW You cannot add an object of a superclass of `Animal` because it *could also be* a list of `Animal`s, to which you cannot add such objects. – Hoopje Jun 24 '15 at 17:10