views:

106

answers:

3

Please help me with this:

If Lion IS-A Animal and given Cage<T>:

Cage<? extends Animal> c = new Cage<Lion>(); // ok,

but

Set<Cage<? extends Animal>> cc = new HashSet<Cage<Lion>>(); // not ok

What I don't see here?

+4  A: 

This is wrong because if it were allowed, then this would be legal:

Set<Cage<? extends Animal>> cc = new HashSet<Cage<Lion>>(); 
cc.add(new Cage<Tiger>()); // legal -- Cage<Tiger> is a Cage<? extends Animal>

Cage<Tiger> is within the bounds of the declaration, but not the definition, so this would crash.

Michael Myers
How can Java decide whether to apply contravariance/covariance or not?
Simon
My understanding Java treats generics as invariant (but, bizarrely, treats arrays as covariant and type checks at runtime) but by writing methods with suitable wildcard (extends or super as appropriate) parameters, and a bit of casting and suppressing unchecked conversions, you can give the impression of whichever is appropriate in your case.
pdbartlett
+2  A: 

You need:

Set<? extends List<? extends Number>> cc = new HashSet<ArrayList<Integer>>();

To explain why... I guess back up to a simpler version of your example:

Number a = new Integer(1); // OK
Set<Number> b = new HashSet<Integer>(); // not OK

this doesn't work because it would allow

b.add(new Double(3.0));
Sean Owen
+1  A: 

When assigning to a variable (Set<T>) with a non-wildcard generic type T, the object being assigned must have exactly T as its generic type (including all generic type parameters of T, wildcard and non-wildcard). In your case T is Cage<Lion>, which is not the same type as Cage<? extends Animal>.

What you can do, because Cage<Lion> is assignable to Cage<? extends Animal>, is use the wildcard type:

Set<? extends Cage<? extends Animal>> a = new Set<Cage<Lion>>();
ILMTitan
+1 for the actual solution.
Michael Myers
Thank you, for me, that was the missing part.
ar_