5

Let's say I have class Animal and class Bear which extends class Animal. Now in constructor I have parameter with Animal:

public class Forest {

   private List<Animal> animals;

   public Forest(List<Animal> list) {
      animals = list;
   }
}

Why I cannot do something like that?

List<Bear> bears = new ArrayList<>();
new Forest(bears);

I thought that Bear is in detail case Animal. Can I fix this only by adding template to Forest<P extends Animal> ?

Jordi Castilla
  • 26,609
  • 8
  • 70
  • 109
Nominalista
  • 4,632
  • 11
  • 43
  • 102

2 Answers2

5

You can use a wild card

  private List<? extends Animal> animals;

   public Forest(List<? extends Animal> list) {
      animals = list;
   }

This lets the list contain any kind of animals, even a mix of different types.

This will all compile:

       List<Bear> bears = new ArrayList<>();
       new Forest(bears);

       ...

       List<Animal> mix = new ArrayList<>();
       mix.add(new Animal());
       mix.add(new Bear());

       new Forest(mix);

The downside is you won't know the type except that they are animals.

There are rules for if and when you can use wildcards and when to use extends vs super depending on if you are putting new elements or not from the Collection.

See the Explanation of the get-put principle which is sometimes called PECS which stands for "Put Extends, Create Super"

EDIT

To be able to add other animals to the list later, you have to change the animal list field to not use a wild card. However, the constructor can still use the wild card but you should probably create a new List object passing in the list from the constructor.

This will work:

private List<Animal> animals;

   public Forest(Collection<? extends Animal> list) {
      animals = new ArrayList<>(list);
   }


   public void add(Animal a){
       animals.add(a);
   }

   public void addAll(Collection<? extends Animal> as){
       animals.addAll(as);
   }

then later

 List<Bear> bears = new ArrayList<>();
 Forest forest = new Forest(bears);

 forest.add(new Animal());

I also added an addAll method which also uses a wildcard

 //same mix and bears lists from before
 Forest forest2 = new Forest(mix);
 forest2.addAll(bears);
Community
  • 1
  • 1
dkatzel
  • 31,188
  • 3
  • 63
  • 67
  • great answer :) one question, in the moment you initialize `new Forest(bear)` you can after add other kind of animals in this forest?? – Jordi Castilla Oct 22 '15 at 15:44
0

you can also use this method

public static <C,T extends C> List<C> convert(List<T> list) {
        List<C> c = new ArrayList<>(list.size());
        c.addAll(list);
        return c;
}

new Forest(convert(bears));