7

I have a question on applying polymorphism: Let's assume I have a class Bird, and I have many classes that extend it (like Pigeon, Falcon and so on). Next, I have a Cage class. In this class, I want to make a list of birds which live in that cage (only one kind of bird can live in each cage).

Because of that, I do not know the extended type of the list (A Pigeon? Or maybe an Eagle?), the only thing i know is that it will be a Bird.

If Pigeon extends Bird

Using polymorphism I can declare a bird as: Bird tom = new Pigeon(); instead of Pigeon tom = new Pigeon();

So why I can't initialize something like that in the constructor: [...]

private List<Bird> birdList;

    public Cage() {
        this.birdList = new ArrayList<Pigeon>();
        /* Instead of birdList = new ArrayList<Bird>(); */
    }

If you cannot do that, is it possible to achieve my goal in another way?

Lii
  • 11,553
  • 8
  • 64
  • 88
Thamiar
  • 600
  • 7
  • 22
  • short answer because they are not the same type. – xiaoyi Apr 27 '15 at 08:23
  • 4
    read this thread http://stackoverflow.com/questions/2745265/is-listdog-a-subclass-of-listanimal-why-arent-javas-generics-implicitly-p –  Apr 27 '15 at 08:24

5 Answers5

8

What you probably want is to use a generic type for your Cage to capture the exact kind of bird that lives in the cage. Like this:

import java.util.ArrayList;
import java.util.List;

public class Cage<T extends Bird> {

    private List<T> birdList;

    public Cage() {
        birdList = new ArrayList<T>();
    }

    public void addBird(T bird) {
        birdList.add(bird);
    }

}

You would then use the cage like this:

Cage<Eagle> eagleCage = new Cage<Eagle>();
eagleCage.addBird(new Eagle());
Arne Deutsch
  • 14,629
  • 5
  • 53
  • 72
4

The reason you can't do List<Bird> birdList = new ArrayList<Pidgeon>() is because then someone could use the birdList reference to write any Bird subclass to your ArrayList<Pidgin>. This must not be allowed by the type checker, a list of Pidgin must contain only Pidgin instances.

Arne gives one solution using a type parameter on Cage. If this is not what to want to do you can declare birdList with a wildcard type.

Your example would look like this:

class Bird {}
class Pidgeon extends Bird {}

class Cage {
    // This is a wildcard type and can contain lists of any
    // subtype to Bird, but you can't add elements to it.
    private List<? extends Bird> birdList;

    public Cage() {
        this.birdList = new ArrayList<Pidgeon>();
    }
}

An upper bounded wildcard type like this tells the compiler birdList contains a list of some unknown subclass to Bird. This has the effect that you can not add any elements to the list since you can not be sure to add the right subtype, and when you read from the list you will get references of type Bird.

Lii
  • 11,553
  • 8
  • 64
  • 88
2

Since in each cage live only one kind of bird, you can use Generic Types to type your Cage class.

For example, Cage<T> will define a list of bird this way : List<T> birdList.

yvilbe
  • 116
  • 5
1

You cannot do:

List birds = new ArrayList();

in java. Generic type on left have to be exacly equal (or may not have to be equal, if wild cards are in game - ? extends T or ? super T) to generic type on right.

If it was possible then it would be impossible to add new Eagle to list declared as list of Bird - that would make no sense

What you can do is:

List<Bird> birds = new ArrayList<Bird>();
birds.add(new Pigeon());

(all family of Bird's, including Pigeon's)

or:

List<? extends Bird> birds = new ArrayList<Pigeon>();
birds.add(new Pigeon()); // Adding is immpossible now - list can be read only now! 
Shifty
  • 303
  • 2
  • 16
-2

You can add to your bird arraylist pidgeon and then test the instance of :

private List<Bird> birdList;

public Cage(){
    this.birdList = new ArrayList<>();
    birdList.add(new Pidgeon());

    //...
    //Checking if it is a Pidgeon :
    if(birdList.get(i) instanceof Pidgeon) {

        //It is a pidgeon

    }
}
hkN
  • 135
  • 4
  • 3
    The whole point of polymorphism is to _not_ do type checking at runtime the way you suggested. Look at yvilbe's answer for a far better solution. – Seelenvirtuose Apr 27 '15 at 08:29