0

With a single layer of inheritance I can do something like this:

// Dog extends Animal
List<Dog> dogs = ...;

// Cat extends Animal
List<Cat> cats = ...;

List<? extends Animal> animals = new ArrayList<>();
animals.addAll(cats);
animals.addAll(dogs);

I can do this without casting, which is nice.

But what if I have a scenario like the following?


// Plant extends LivingBeing
List<Plant> plants = ...;

// Animal extends LivingBeing
// Cat extends Animal
List<Cat> cats = ...;

List<? extends LivingBeing> livingThings = new ArrayList<>();
// This is fine
lvingThings.addAll(plants);

// FIXME: Fails because Cat doesn't directly extend LivingBeing
livingThings.addAll(cats);

Is there a way to specify an upper bound that is not a direct parent of all members of the list, so that I can avoid casting?

TheFunk
  • 981
  • 11
  • 39
  • 2
    Why don't you use a `List`? – Olivier Apr 27 '22 at 18:36
  • As an aside: the current approach violates [PECS](https://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super). The collections `animals` and `livingThings` act as consumer in the code shown. – Turing85 Apr 27 '22 at 18:45
  • 1
    The first example doesn't actually work. In both examples, the correct thing is to remove `? extends`. – Louis Wasserman Apr 27 '22 at 18:57
  • 1
    None of the examples that you claim to work, actually work. Did you actually test these code snippets? – Sweeper Apr 27 '22 at 19:00
  • 1
    You're probably looking for `? super LivingBeing` instead. See also [PECS](https://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super). – Sweeper Apr 27 '22 at 19:02

1 Answers1

3

Your first example doesn't compile. Both are caused by the ? extends part. The issue is that as soon as the initialization is done, the compiler already forgot the actual type. That means that after initializing livingThings, the compiler thinks it can be a List<LivingBeing>, List<Animal> or List<Cat> (all of which would allow adding cats), but also List<Dog> or List`, which don't allow cats.

If you want a list with things that can be any type of living being, you must declare it as such: List<LivingBeing>. Otherwise you can't add anything anymore except null.

The latter is true for any Collection<? extends T> for any type T. The only value that is safe for any type that matches ? extends T is null.

Rob Spoor
  • 6,186
  • 1
  • 19
  • 20
  • 1
    In general: `? extends T` allows you to retrieve elements as type `T` without casting, but you can't add anything (except for `null`). `? super T` allows you to add anything that's `T` or a sub type, but you can only retrieve it as `Object`. – Rob Spoor Apr 27 '22 at 18:39
  • Had a brain lapse for a moment I think. I think for some reason I was assuming I add to a List extends T> when mapping differing objects but in reality I just add to a List and then access as a List extends T>. Thank you! – TheFunk Apr 28 '22 at 12:24