1

I have classes:

class Animal{
    public void type(){
        System.out.println("I am Animal");
    }
}
class Dog extends Animal{
    public void type(){
        System.out.println("I am Dog");
    }
}
class Cat extends Animal{
    public void type(){
        System.out.println("I am Cat");
    }
}
class Haski extends Dog{
    public void type(){
        System.out.println("I am Haski");
    }
}

and I create List with wildcards:

List<? extends Animal> animalList = new ArrayList<Animal>();

I know that I can not add some object to animalList. I read about it in different books, articles in internet, video lessons, but I still not understand why? If we knot that animalList contains only objects extends Animal why java can not add any objects extends Animal and cast it to Animal?

animalList.add(new Dog()); //cast dog to Animal
animalList.add(new Cat()); //cast cat to Animal

Compiler has enougth information - objects extends Animal why it can not cast?

EDIT:

So I do not understand rightly.

List<Animal> animalList1 = new ArrayList<Animal>();
animalList.add(new Animal());
animalList.add(new Dog());
animalList.add(new Cat());
animalList.add(new Haski());

and

List<? extends Animal> animalList

There is a feeling that they should be the same. But I can not feel the difference in principle

ip696
  • 6,574
  • 12
  • 65
  • 128
  • 1
    `List extends Animal> animalList` isn't required to hold only `ArrayList();`. It can also hold `List` (list designed to hold only `Dog`s or its subtypes) so would you want compiler to allow you to add `Cat` object to such list? – Pshemo Aug 05 '18 at 07:22
  • 1
    Think of `? extends Animal` as a very specific (but unknown) class that extends `Animal`. Your Problem of understanding arises because you think it means any class extending `Animal`. – Henry Aug 05 '18 at 07:26
  • `List` and `List extends Animal>` are not the same type. You can assign a `List` to a `List extends Animal>` variable but you cannot assign it to a `List` variable. – Henry Aug 05 '18 at 07:38

1 Answers1

3

Because of this:

List<Dog> dogList = new ArrayList<>(); // compiles
List<? extends Animal> listOfUnknownAnimalType = dogList; // compiles
listOfUnknownAnimalType.add(new Cat()); // doesn't compile

If the third line compiled, you would be able to add a Cat to a List<Dog>, and that would completely ruin the type-safety that generics bring: listOfUnknownAnimalType is initialized with dogList at line 2. So both variables reference the same list, which is a List<Dog>. So if you add a Cat to listOfUnknownAnimalType, you add it to dogList. Adding a Cat to a list of dogs isn't right: a List<Dog> is supposed to contain only dogs, not cats.

Imagine you have this method:

public void printAllTypes(List<Animal> list) {
    list.forEach(a -> System.out.println(a.type()));
}

This is fine and works well:

List<Animal> list = new ArrayList<>();
list.add(new Cat());
list.add(new Dog());
printAllTypes(list);

Now suppose you have this instead:

List<Dog> list = new ArrayList<>();
list.add(new Haski());
list.add(new Dog());
printAllTypes(list);

The last line doesn't compile, because a List<Dog> is not a List<Animal>(for the same reason as above). That's where wildcards become useful: since your method doesn't actually care about the concrete generic type of the list, as long as it extends Animal, and since it doesn't mutate the list, you can rewrite your method to

public void printAllTypes(List<? extends Animal> list) {
    list.forEach(a -> System.out.println(a.type()));
}

And now you can call it with a List<Animal>, but also with a List<Dog> or a List<Cat> as argument.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • OK. When we call get method listOfUnknownAnimalType.get() we get Animal anyway. even though in 2 line we add dogs to this list. If we add Cat in 3 line - How will it break type-safety if when we call listOfUnknownAnimalType.get() on Cat we get Animal? Cat extend Animal – ip696 Aug 05 '18 at 07:30
  • Read the code again. `listOfUnknownAnimalType` is initialized with `dogList`. So both variables reference the same list, which is a List. So if you add a Cat to listOfUnknownAnimalType, you add it to dogList. Adding a Cat to a list of dogs isn't right: a List is supposed to contain only dogs, not cats. – JB Nizet Aug 05 '18 at 07:34
  • is this useful only if I pass an list as an argument in the method? or are there other uses? – ip696 Aug 05 '18 at 07:41
  • 1
    Yes, wildcards are mostly useful in method argument types. – JB Nizet Aug 05 '18 at 07:42