I know that there are a lot of questions about this topic, but unfortunately they couldn't help me to eliminate my obscurities. First of all, look at the following example. I don't understand, why the following "add"-method someCage.add(rat1) doesn't work and aborts with the following exception:

Exception in thread "main" java.lang.Error: Unresolved compilation
problem: The method add(capture#2-of ? extends Animal) in the type
Cage is not applicable for the
arguments (Rat)

Is this the same reason why Cage<Rat> is not a Cage<Animal>? If yes, I don't understand it in this example, so I'm not sure what the compiler exactly does. Here is the code example:

Moreover, I would be pleased if you could give me a meaningful "super" example with this animal-cage-code. Until now I haven't understood how to use it. There are a lot of theoretical examples and I read about the PECS concept but anyhow I wasn't able to employ it in a meaningful matter yet. What would it mean to have a "consumer" (with super) in this example?

Good example, thanks. It's already much clearer to me now. The only thing which still confuses is the invocation cage.cage. cage.addAll doesn't work, cage.cage.addAll does, but I'm not sure how to read cage.cage.addAll. Is this now a SUPER invocation like invoking a super method or something?
–
BevorJul 26 '11 at 15:03

1

The reason it's confusing is your example had an unfortunate field name for cage, which is the same as the Class name - the usual variable name for a Cage is also case. I have updated the answer to use a different field name: pen.
–
Bohemian♦Jul 26 '11 at 21:21

Ok, it's almost completely clear to me now, thanks. But the only thing I don't understand is why I don't need a getter-method to invoke "pen" in cage.pen.addAll(this.pen)? Is this some kind of reflection thing?
–
BevorJul 27 '11 at 8:08

Because a Cage's pen is visible to all (other) instances of Cage: private means visible within the class - not necessarily within the same instance
–
Bohemian♦Aug 7 '11 at 9:29

You can add a Rat to a Cage<Animal>, because a Rat "is" an Animal (extends Animal).

You cannot add a Rat to a Cage<? extends Animal>, because <? extends Animal> might be <Lion>, which a Rat is not.

In other words:

Cage<? extends Animal> cageA = new Cage<Lion>(); //perfectly correct, but:
cageA.add(new Rat()); // is not, the cage is not guaranteed to be an Animal or Rat cage.
// It might as well be a lion cage (as it is).
// This is the same example as in Kaj's answer, but the reason is not
// that a concrete Cage<Lion> is assigned. This is something, the
// compiler might not know at compile time. It is just that
// <? extends Animal> cannot guarantee that it is a Cage<Rat> and
// NOT a Cage<Lion>
//You cannot:
Cage<Animal> cageB = new Cage<Rat>(); //because a "rat cage" is not an "animal cage".
//This is where java generics depart from reality.
//But you can:
Cage<Animal> cageC = new Cage<Animal>();
cageC.add(new Rat()); // Because a Rat is an animal.

Imagine having your Cage<? extends Animal> created by an abstract factory method, which gets implemented by a subclass. In your abstract base class you cannot tell which type actually gets assigned, neither can the compiler, because maybe the concrete class gets only loaded at runtime.

That means, the compiler cannot rely on Cage<? extends Animal> to not be a Cage of some other concrete subtype, which would make the assignment of a different subtype an error.

Both answers so far have been great. I'd just like to add a tidbit to help your understanding of them.

To further Ron's answer, you may be thinking the following:

"why is it that that someCage.add(rat1) becomes a Cage<? extends Animal>.add(rat1)? Can't someCage point to any Cage of any type which extends Animal (and I've now set it to point to a cage of rats?)"

Totally legitimate question. Thing is, when you do the someCage = ratCage, an element-by-element copy is done from ratCage into someCage. So in fact, you have not simply set someCage to now point to a ratCage. In actuality, someCage is still a Cage<? extends Animal>. You can't do someCage.add(rat1) because you don't know the type of the Cage, only that it's type is bounded above by Animal.