Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parameter extending appropriate parameter in Java

Tags:

java

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> ?

like image 427
Nominalista Avatar asked Oct 22 '15 15:10

Nominalista


People also ask

What are the types of parameters in Java?

Parameter Types We can use any data type such as primitive data types including int, float, double, char, short, byte, String, and object reference variables for a parameter of a method and constructor. There is no standard limit to specify the number of parameters in the definition of a method.

What are parameters in Java?

A parameter is a variable used to define a particular value during a function definition. Whenever we define a function we introduce our compiler with some variables that are being used in the running of that function. These variables are often termed as Parameters.

What are bounded type parameters in Java?

There may be times when you want to restrict the types that can be used as type arguments in a parameterized type. For example, a method that operates on numbers might only want to accept instances of Number or its subclasses. This is what bounded type parameters are for.

What is difference between extends and super in generics?

extends Number> represents a list of Number or its sub-types such as Integer and Double. Lower Bounded Wildcards: List<? super Integer> represents a list of Integer or its super-types Number and Object.


1 Answers

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);
like image 93
dkatzel Avatar answered Sep 28 '22 08:09

dkatzel