I know the answer to the problem below is statement 1 and 3. But I can't explain why statement number 2 would not work. Could someone please enlighten me? Thanks.
Consider the following class definitions.
public class Animal
{
public void eat()
{ /* implementation not shown */ }
// constructors and other methods not shown
}
public class Tiger extends Animal
{
public void roar()
{ /* implementation not shown */ }
// constructors and other methods not shown
}
Assume that the following declaration appears in a client class.
Animal a = new Tiger();
Which of the following statements would compile without error?
I. a.eat();
II. a.roar();
III. ((Tiger) a).roar();
I'm assuming that instantiating 'a' as a new Tiger() would allow 'a' to access the roar() method. But I'm obviously wrong. I think I'm not understanding the polymorphic nature of the problem well.
CodePudding user response:
Because 'a' is referenced as an Animal. As far as the code is concerned, it only has direct access to the methods defined by Animal. Your third line works because you're explicitly casting it as a Tiger thereby changing the context and giving it access to Tiger methods so long as there isn't a ClassCastException.
The way such a class should be designed is to make Animal an abstract class and then give it an abstract "speak" method something like:
public abstract class Animal {
public abstract void speak();
}
then in your Tiger class
public class Tiger extends Animal {
public void speak() {
System.out.println("Roar.");
}
}
Then you can call
Animal a = new Tiger();
a.speak();
Then you could also create a dog
public class Dog extends Animal {
public void speak() {
System.out.println("Woof.");
}
}
Animal a = new Dog();
a.speak();
CodePudding user response:
There are a number of questions like this on SO and the answer is always that Java considers the static type to be Animal
so calls to methods of Tiger
will not compile, but I think it might be useful to say a little more about why.
Consider:
Animal a = new Tiger();
a.roar();
It is very easy to see that while a
is declared as an Animal
, it can only refer to a Tiger
at the point of the call to roar()
. But consider a more complex example:
Animal a = new Tiger();
// . . .
if (zoo.getType() == ZooType.TROPICAL) {
a = new Toucan();
}
for (Condition c : getMeteorologicalConditions()) {
// . . .
if (season == Seasons.WINTER) {
a = new Penguin();
}
// . . .
}
// . . .
a.roar();
Is it safe to call roar()
here? When is it safe? Can the compiler prove, statically, at compile time, that a
will always be a Tiger
? Maybe somehow it can be shown that this code will never be called for a tropical zoo, or in the winter (at least if there are any applicable meteorological conditions; otherwise, the loop would execute zero times and winter wouldn't matter). But can you imagine the complexity of proving that?
The compiler doesn't even try. When you tell it a
is an Animal
, it says, "Okay, it can hold any type of Animal." And it only permits you to call methods that are valid for all animals.