MBlanc
MBlanc

Reputation: 1783

What's the difference between these generic declarations?

How would you describe – in plain english – the difference between the contents of these lists?

I'm looking for a simple comparison that can be used as a reference.

/* 1 */ List<          List<          Dog>>
/* 2 */ List<          List<? extends Dog>>
/* 3 */ List<? extends List<          Dog>>
/* 4 */ List<? extends List<? extends Dog>>

The generic declarations with super are similar. So what about these:

/* 5 */ List<? super   List<? extends Dog>>
/* 6 */ List<? extends List<? super   Dog>>

Related questions & resources

Upvotes: 2

Views: 137

Answers (3)

awksp
awksp

Reputation: 11877

  1. A List whose elements are lists of dogs
  2. A List whose elements are lists of a type that extends Dog
  3. A List whose elements are a single subtype of a list of dogs
  4. A List whose elements are a single subtype of a (list of a type that extends Dog)
  5. A List whose elements are a single supertype of a (list of a type that extends Dog)
  6. A List whose elements are a single subtype of a (list of a type that super Dog)

Where "subtype", "supertype", "super", and "extends" are the "generic" versions (i.e. also includes the bounding type)


Examples with Number and subtypes, because why not. Replace Dog with Number.

  1. List<List<Number>> might look like a 2D array of Number elements. Pretty simple.

  2. List<List<? extends Number>> might look like a 2D array, where each row is a different subtype of number. For example, the first row might be a List<Integer>, the second row might be a List<Double>, etc.

  3. List<? extends List<Number>> might be a List<ArrayList<Number>>, List<List<Number>>, List<LinkedList<Number>>, etc. The Number has to stay, because generics are invariant, but you can have List or any of its subtypes as the "overall" type of the elements. You can only pick one of List or its subtypes though, and the one you pick you have to stay with.

  4. List<? extends List<? extends Number>> is similar to List<? extends List<Number>>, except now you can pick Number or any of its subclasses as the elements of the "overall" 2D array. So you can have List<List<Integer>>, List<ArrayList<Integer>>, List<LinkedList<Double>>, etc. As before, you can only pick one of List's subtypes and one of Number's subtypes.

  5. (tricky!) List<? super List<? extends Number>> appears to be equivalent to List<List<? extends Number>>, List<Collection<? extends Number>>, etc. but not List<List<Number>> or anything concrete where a subtype of Number is used. I think this is because List<Number> isn't considered a supertype of List<? extends Number>, which I suppose makes sense due to generics being invariant. List<Object> as well as raw types (List<List>, List<Collection>, etc.) also works.
  6. Same thing as 4, except you get either List<Number> or List<Object> (and apparently List<Serializable>) as the inner list.

As @MadProgrammer said, due to PECS (Producer-Extends-Consumer-Super), any time you have a ? extends for your generic type you won't be able to update the list, only retrieve items from it. So no add()-ing and no set()-ing.

Upvotes: 7

laune
laune

Reputation: 31300

Replace each "<" by "of".

Whenever there is a "? extends X", insert "or one of its subclasses" after X.

Upvotes: 1

Patrick Collins
Patrick Collins

Reputation: 10604

The first one is concrete: it's a List of Lists of Dog, and nothing else.

The second one is abstract on the innermost declaration: it's a List of Lists of some class that inherits from Dog -- say Poodle. It might be a List<ArrayList<Poodle>>.

The third one is abstract on the inner list declaration: its a List of some sort of container that inherits from List -- maybe ArrayList -- of Dog. Then you might have List<ArrayList<Dog>>.

The fourth one is abstract on both: its a List of some kind of container derived from List, filled with some animal that inherits from Dog -- maybe List<ArrayList<Poodle>>.

Upvotes: 1

Related Questions