I try to understand how subtyping and inheritance works in Java. Recently I ran into a situation that confuses me. I have got the following Code with three classes:
class Super{
String i = "Variable of superclass";
void m() {
System.out.println(i);
}
}
class Sub extends Super{
String i = "Variable of subclass";
/*@Override
void m() {
System.out.println(i);
}*/
}
public class Main {
public static void main(String[] args) {
Sub sub = new Sub();
sub.m(); //Prints Variable of superclass
}
}
I am so confused with the output of m(). I just don't understand why the method, which is called on an instance of Sub, uses the variable of the superclass. I guess it has to do with variable hiding / shadowing but I can't figure out what is going on there in detail. If I do something like
Super sup = new Sub();
sup.m();
then I understand, why it prints the variable of the superclass. That's because variables are bound in a static way which I know as variable hiding / shadowing. But I really don't get, what happens in my example.
I was even more confused about what happened when I removed the comment in Sub. I understand that the method is now overridden but I don't get why the method now uses the variable of the subclass even though it's exactly the same code that would have been inherited if I left the comment. I mean, it was already there! How does overriding a method with the exact same code change anything?
I also tried adding System.out.println(this.getClass.getName());
inside the method to make sure that the method is really called on an instance of sub. And yes, it is.
If anyone could help me out understanding, what's going on here, I would be very thankful!
Methods can be overriden, but fields cannot. Sub
does not define a m
method, so sub.m();
calls the m
method from Super
. In the scope of that method, only the i
field defined in Super
itself is visible.
The i
field defined in Sub
is a distinct field that shadows the field from the superclass of the same name. If you had instead defined a getI()
method in both Super
and Sub
, calling getI()
in Super
(when the underlying instance is of type Sub
) would use the method defined in Sub
.