I see that the question has already been answered a number of times, just want to put in my inputs on the same question.
Lets us go ahead and create a simplified Animal class hierarchy.
abstract class Animal {
void eat() {
System.out.println("animal eating");
}
}
class Dog extends Animal {
void bark() { }
}
class Cat extends Animal {
void meow() { }
}
Now let us have a look at our old friend Arrays, which we know support polymorphism implicitly-
class TestAnimals {
public static void main(String[] args) {
Animal[] animals = {new Dog(), new Cat(), new Dog()};
Dog[] dogs = {new Dog(), new Dog(), new Dog()};
takeAnimals(animals);
takeAnimals(dogs);
}
public void takeAnimals(Animal[] animals) {
for(Animal a : animals) {
System.out.println(a.eat());
}
}
}
The class compiles fine and when we run the above class we get the output
animal eating
animal eating
animal eating
animal eating
animal eating
animal eating
The point to note here is that the takeAnimals() method is defined to take anything which is of type Animal, it can take an array of type Animal and it can take an array of Dog as well because Dog-is-a-Animal. So this is Polymorphism in action.
Let us now use this same approach with generics,
Now say we tweak our code a little bit and use ArrayLists instead of Arrays -
class TestAnimals {
public static void main(String[] args) {
ArrayList<Animal> animals = new ArrayList<Animal>();
animals.add(new Dog());
animals.add(new Cat());
animals.add(new Dog());
takeAnimals(animals);
}
public void takeAnimals(ArrayList<Animal> animals) {
for(Animal a : animals) {
System.out.println(a.eat());
}
}
}
The class above will compile and will produce the output -
animal eating
animal eating
animal eating
animal eating
animal eating
animal eating
So we know this works, now lets tweak this class a little bit to use Animal type polymorphically -
class TestAnimals {
public static void main(String[] args) {
ArrayList<Animal> animals = new ArrayList<Animal>();
animals.add(new Dog());
animals.add(new Cat());
animals.add(new Dog());
ArrayList<Dog> dogs = new ArrayList<Dog>();
takeAnimals(animals);
takeAnimals(dogs);
}
public void takeAnimals(ArrayList<Animal> animals) {
for(Animal a : animals) {
System.out.println(a.eat());
}
}
}
Looks like there should be no problem in compiling the above class as the takeAnimals() method is designed to take any ArrayList of type Animal and Dog-is-a-Animal so it should not be a deal breaker here.
But, unfortunately the compiler throws an error and doesn't allow us to pass a Dog ArrayList to a variable expecting Animal ArrayList.
You ask why?
Because just imagine, if JAVA were to allow the Dog ArrayList - dogs - to be put into the Animal ArrayList - animals - and then inside the takeAnimals() method somebody does something like -
animals.add(new Cat());
thinking that this should be doable because ideally it is an Animal ArrayList and you should be in a position to add any cat to it as Cat-is-also-a-Animal, but in real you passed a Dog type ArrayList to it.
So, now you must be thinking the the same should have happened with the Arrays as well. You are right in thinking so.
If somebody tries to do the same thing with Arrays then Arrays are also going to throw an error but Arrays handle this error at runtime whereas ArrayLists handle this error at compile time.