Reputation: 358
Recently I stumbled upon Java tests and found a strange behaviour.
class MainOne {
private int i = 5;
public MainOne() {
System.out.println("MainOne says that I is " + getI());
}
public int getI() {
System.out.println("MainOne getI was used");
return i;
}
}
public class TheTest extends MainOne {
private static int i = 10;
public TheTest() {
System.out.println("TheTest says that I is " + super.getI());
}
public int getI() {
System.out.println("TheTest getI was used");
return i;
}
public static void main(String[] args) {
TheTest test = new TheTest();
}
}
Result was:
TheTest getI was used
MainOne says that I is 10.
MainOne getI was used
TheTest says that I is 5
Question is, what happened? How come Base class uses it descendant's method?
Upvotes: 4
Views: 4846
Reputation: 3938
There are two important points to note in this problem.
super.getI() invocation inside MainOne
public TheTest() {
System.out.println("TheTest says that I is " + super.getI());
}
forcefully this invocation goes to super class even though the method is overridden.
Field i
in subclass is defined as a static
field
There is a difference in the results based on whether this field is a static or not. When the overridden getI()
is invoked from TheTest
constructor, the invocation goes to the MainOne
classes method. However when this invocation happens, instance fields of TheTest
class is not yet initialized to the assigned values (but only default values).
If the field i
in TheTest
is a instance field, getI()
will print 0 instead of 10.
Details of the sequence of invocation is hard to represent here. However in case you wanted to find further details, please refer below.
new TheTest()
-> super() - implicitly invoke default constructor
-> inside MainOne() constructor
-> System.out.println("MainOne says that I is " + getI());
-> this results in a getI() invocation
-> since getI() is overridden by subclass
-> invoke on TheTest instance, not MainOne class.
-> TheTest's instance fields not given the assigned values
-> TheTest's static fields have got the assigned values
<- string concatenation completes for System.out.println()
<- MainOne constructor (or the super) is completed
<- super() completes
-> System.out.println("TheTest says that I is " + super.getI());
-> this results in a getI() invocation
-> however explicit super.getI() forcefully goes to super class
-> invoke on MainOne (super) instance, not TheTest class.
<- string concatenation completes for System.out.println()
<- new TheTest() constructor completes
Hope this helps you in understanding the details further.
Upvotes: 0
Reputation: 279910
This is all about order of execution and constructor inheritance. TheTest
constructor implicitly calls the super
MainOne
constructor.
so
public TheTest() {
System.out.println("TheTest says that I is " + super.getI());
}
calls
public MainOne() {
System.out.println("MainOne says that I is " + getI());
}
which calls the overriden getI()
because of polymorphism.
public int getI() {
System.out.println("TheTest getI was used");
return i;
}
The i
here is the static i
declared in TheTest
. Finally
super.getI());
gets called which uses MainOne
's i
.
You therefore get
TheTest getI was used
MainOne says that I is 10.
MainOne getI was used
TheTest says that I is 5
Note that polymorphism does not apply to fields and that fields (whether static
or instance) may hide fields of the same name in parent classes.
Upvotes: 6