-4

Java don't allow putting multiple subtype of a superType into a list if <? extends superType> is the declared generic type of the list. Because in this case lists are treated as read only to enforce it contains only objects of it's dynamic type.

Line commented by //1 going to return the compile error.

My question is:

  • why would line commented by //2 compiles? It eventually puts multiple subType objects into the list.
  • in case it's not a problem here then why throw a compile error at //1
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

class Scratch {
    static class Animal {}
    static class Dog extends Animal {}
    static class Cat extends Animal {}

    public static void main(String[] args) {
        List<Cat> cats = new ArrayList<>();
        List<Dog> dogs = new ArrayList<>();

        //List<? extends Animal> animalsOfTwoTypes = new ArrayList<Dog>(); 
        //animalsOfTwoTypes.add(new Dog()); //1


        List<? extends Animal> animals = Stream.of(cats, dogs)  //2
                .flatMap(Collection::stream)
                .collect(Collectors.toList());
    }
}
crs
  • 3
  • 3
  • 2
    Does this answer your question? [Difference between super T> and extends T> in Java](https://stackoverflow.com/questions/4343202/difference-between-super-t-and-extends-t-in-java) – IQbrod Sep 22 '20 at 13:44
  • Line 1 does not compile as `new Dog()` would not fit in a `List` which is a correct `List extends Animal>` – IQbrod Sep 22 '20 at 13:46
  • 2
    See also [What is PECS (Producer Extends Consumer Super)?](https://stackoverflow.com/q/2723397/3890632) – khelwood Sep 22 '20 at 13:48

1 Answers1

2

It eventually puts multiple subType objects into the list.

Yes, but they're all subtypes of Animal. You've not specified what the type of the elements in the list should be; all the compiler is doing is checking that the resulting type of the list is compatible with the variable type.

You could have written:

    List<Animal> animalsWithoutWildcard = Stream.of(cats, dogs)  //2
            .flatMap(Collection::stream)
            .collect(Collectors.toList());

and this would be fine also. And then

    List<? extends Animal> animals = animalsWithoutWildcard;

would also be fine, because a list of Animals can also be used as a list from which you can obtain instances of Animal: animals is a producer of Animal instances, whereas animalsWithoutWildcard is both a producer and a consumer of Animal instances.

The point isn't that the thing referred to by a List<? extends Animal> variable can't have things added to it (aside from null), it is that it can't have things added to it through that variable. So:

System.out.println(animals.size());     // N
animalsWithoutWildcard.add(new Dog());  // fine
System.out.println(animals.size());     // N + 1

// but this is an error, even though it's the same list.
animals.add(new Dog());
Andy Turner
  • 122,430
  • 10
  • 138
  • 216
  • 1
    *can't have things added to it* -> except `null`. `List extends Animal>` is not immutable, the compiler just forbids to add any `Object` to it. (but `null` is not an object) – Lino Sep 22 '20 at 13:52
  • 1
    @Lino sure. But even if you couldn't add `null` to it, you could invoke `remove()`, `clear()`, etc, so it wouldn't be immutable anyway. – Andy Turner Sep 22 '20 at 14:16