7

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?

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501
ar_
  • 73
  • 2

3 Answers3

6

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
  • 188,989
  • 46
  • 291
  • 292
  • How can Java decide whether to apply contravariance/covariance or not? – Simon May 20 '10 at 18:21
  • 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 May 20 '10 at 18:33
6

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
  • 10,751
  • 3
  • 30
  • 46
2

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
  • 66,182
  • 23
  • 141
  • 173