I give lessons on the fundamentals of the Java programming language, to students who study this subject in college.
Today one of them got me really confused with her question, so I told her to give me just a day to think about the problem, and I'll give her as accurate of an answer as I can.
She told me that the teacher got really angry when she used the keyword instanceof
in her exam.
Also, she said that the teacher said that there is not a way to prove how polymorphism worked if she used that word.
I thought a lot to try to find a way to prove that in some occasions we need to use instanceof
, and also that even if we use it, there still some polymorphism in that approach.
So this is the example I made:
public interface Animal
{
public void talk();
}
class Dog implements Animal {
public void talk() {
System.out.println("Woof!");
}
}
public class Cat implements Animal
{
public void talk() {
System.out.println("Meow!");
}
public void climbToATree() {
System.out.println("Hop, the cat just cimbed to the tree");
}
}
class Hippopotamus implements Animal {
public void talk() {
System.out.println("Roar!");
}
}
public class Main {
public static void main(String[] args) {
//APPROACH 1
makeItTalk(new Cat());
makeItTalk(new Dog());
makeItTalk(new Hippopotamus());
//APPROACH 2
makeItClimbToATree(new Cat());
makeItClimbToATree(new Hippopotamus());
}
public static void makeItTalk(Animal animal) {
animal.talk();
}
public static void makeItClimbToATree(Animal animal) {
if(animal instanceof Cat) {
((Cat)animal).climbToATree();
}
else {
System.err.println("That animal cannot climb to a tree");
}
}
}
My conclusions are the following:
The first approach (APPROACH 1) is a simple demo of how to program to an interface, not a realization. I think that the polymorphism is clearly visible, in the parameters of the method makeItTalk(Animal animal)
, and also in the way the method talk is called, by using the animal object.(This part is ok)
The second part is the one that makes me confused. She used instanceof
at some point in her exam (I don't know how their exam looked like), and that was not accepted correctly because the teacher said, you are not proving polymorphism.
To help her understand when she can use instanceof
, I thought about telling her, that she can use it, when the method she needs to call is not in the interface, but it is just in one of the implementing classes.
As you can see, only cats can climb to trees, and it would not be logical to make a Hippopotamus or a Dog climb to a tree. I think that could be an example of when to use instanceof
But what about polymorphism in approach 2?
How many uses of polymorphism do you see there (only approach 2)?
Do you think this line has some type of polymorphism in it?
((Cat)animal).climbToATree();
I think it does, because in order to achieve a Casting of this type, the objects need to have an IS-A relationship, an in some way that is polymorphism.
What do you think, is it correct?
If yes, how would you explain with your own words, that casting relies on polymorphism?
In your above example, there is no need to call
makeItClimbToATree (new Hippopotamus ());
It could be easily avoided, if makeItClimbToATree wouldn't expect an animal, but something more specific, which is really able to climb a tree. The necessity to allow animals, and therefore to use instanceof, isn't visible. If you manage the animals in a List of animals, it will be more obvious.
While ircmaxells explanation starts great, while introducing the Koala and other TreeClimbers, he doesn't see a second extension which is hiding in a sea anemone: different capabilities of animals like seaAnemoneHider, winterSleeping, blueEyed, bugEating, and so on, and so on. You would end up with boolean over boolean, constantly recompiling the base class, as well as breaking extending customer classes, which would need recompilation again, and wouldn't be able to introduce their own possibilities in a similar manner.
Customer A would need Customer B to declare a NotBugEatingException, to get your behaviour into the base class.
Introducing your own interfaces, combined with instanceof, is a much cleaner approach, and more flexible. Customer A might define divingLikeAPenguin and customer B trumpeting, both not knowing of each other, both not affecting the Animal class and not provoking useless recompilations.
import java.util.*;
interface Animal {
public void talk ();
}
interface TreeClimbing {
public void climbToATree ();
}
class Dog implements Animal {
public void talk () { System.out.println("Woof!"); }
}
class Cat implements Animal, TreeClimbing {
public void talk () { System.out.println("Meow!"); }
public void climbToATree () { System.out.println ("on top!"); }
}
public class TreeCriterion {
public static void main(String[] args) {
List <Animal> animals = new ArrayList <Animal> ();
animals.add (new Cat ());
animals.add (new Dog ());
discuss (animals);
upTheTree (animals);
}
public static void discuss (List <Animal> animals) {
for (Animal a : animals)
a.talk ();
}
public static void upTheTree (List <Animal> animals) {
for (Animal a : animals) {
if (a instanceof TreeClimbing)
((TreeClimbing) a).climbToATree ();
}
}
}
We don't need a third animal, dog and cat are enough. I made them default visible instead of public, to make the whole example fit into a single file.