I am learning Java and have been playing with up and downcasting in my command prompt using the classes I defined in Eclipse. I am working with three classes that I included here, and my question is at the bottom:
public class Animal {
public static void main(String[] args) {
// TODO Auto-generated method stub
}
public void eat() {
System.out.println("Yummy");
}
public void sleep() {
System.out.println("I like sleep.");
}
}
public class Mammal extends Animal {
public static void main(String[] args) {
// TODO Auto-generated method stub
}
public void controlTemp() {
System.out.println("Temperature regulation ");
}
}
public class Lion extends Mammal {
public static void main(String[] args) {
// TODO Auto-generated method stub
}
@Override
public void sleep() {
super.sleep();
System.out.println("Especially on the grass.");
}
@Override
public void controlTemp() {
super.controlTemp();
System.out.println("I'm an endotherm.");
}
public void roar() {
System.out.println("ROARRRRR!");
}
}
Here is what I did in jshell in my cmd:
jshell> Animal a1 = new Lion();
a1 ==> Lion@4afc40bb
| created variable a1 : Animal
jshell> ((Mammal)a1).controlTemp();
Temperature regulation
I'm an endotherm.
jshell> ((Lion)a1).controlTemp();
Temperature regulation
I'm an endotherm.
My question is why does casting a1 to Mammal give me the result of the overridden controlTemp() method in my Lion class instead of the one in the Mammal class?
Because all non-static methods in java are always resolved via a thing called dynamic dispatch.
To be slightly more specific, what method signature you actually select depends on the type of the thing you invoke it on. The expression ((Mammal) a1)
is of type Mammal
(not because a1
is a lion; it's because anything cast to Mammal is an expression of type Mammal).
But, for a given signature, the actual method you get? That depends entirely on the runtime type of the actual object that your expression references.
At the runtime level, methods don't just have a name: Their full signature includes their parameter types, their name, and their return type.
As your @Override
annotation indicates, the public void controlTemp()
in your Lion class has the same name, the same argument types (namely: noa arguments), and the same return type (namely: void
), and thus it overrides the void controlTemp()
method in Mammal
.
This gets us to the following flow:
controlTemp()
method as defined in Mammal, and this is compiled into your class file. In other words, during compilation, it's all about Mammal. That's what the expression's type is.a1
ref is pointing at is checked, is found to be an instance of Lion
, and the most specific version of the controlTemp
method that any instance of Lion
has, is the one defined in Lion
. So that one gets invoked.So how do you invoke the one from animal? not possible, except from within public class Lion extends Mammal {}
, where you can use super.controlTemp()
to invoke it. Anywhere else, you simply cannot do so (at least, not without raw class hackery, which is well beyond scope of the level you're at and not something anybody writing in java-the-language ever does for good reason).