**Java code-MySuper class is the parent class and is extended by MySub class.I am creating the object MySub mySub = new MySub().I thought the output would be "y" but when I tried to run the code it was showing null as an output **
class MySuper {
String strl = "x";
public MySuper() {
myMethod();
}
void myMethod() {
System.out.print(strl);
}
}
class MySub extends MySuper {
String str2 = "y";
void myMethod() {
System.out.print(str2);
}
public static void main(String[] args) {
MySub mySub = new MySub();
}
}
CodePudding user response:
The implicit constructor used by MySub
looks like this:
class MySub extends MySuper {
String str2;
MySub() {
super();
str2 = "y";
}
// rest omitted for brevity
}
Field initialization is "folded into" the constructor. But the super constructor is invoked first. So, when the super constructor invokes the myMethod()
method, which is overridden by the subclass, the str2
field has not been assigned a non-default value yet. This results in your code printing out null
.
And this is why you should avoid invoking overridable methods in a constructor.
CodePudding user response:
This is due to Java dynamic method dispatch.
In your example. when you're instancing with the default constructor a MySub
instance, this implicitly performs a call to the superclass' constructor. Within the superclass' constructor there is a call to myMethod()
which is declared in MySuper
and overridden in MySub
.
Now, even if myMethod
is being called from the superclass' constructor, which could fool us into thinking that the superclass' version of myMethod
is being called, this is not what is actually happening.
The instance that is performing all these calls is still a MySub
instance; therefore the myMethod
implementation that is being invoked by Java is the closest implementation to MySub
, i.e. the myMethod
overridden by MySub
.
So, if in this case the myMethod
version that is being invoked in the superclass' constructor is the one overridden by MySub
, why it's not printing str2
value? This is because, although method calls are dynamically dispatched, fields are statically dispatched. In Java, only methods can override their base class' methods. Child class' fields can only shadow base class' fields (with the same name). So Java already knows at compile time which fields are being used and only at run time which method implementations are being invoked. This means that when you're using a field in a child class, you can be sure that the one you're using is always the one you're referring to.
Getting back to your example, since myMethod
is being invoked from the superclass' constructor, at this point we're still initializing the parent object, the child class' fields have not been initialized yet, they still contain their default values (the default value for references is null). So, in your dynamically dispatched myMethod
we are printing the value of the statically dispatched str2
which is still at null because the child class initialization has not happened yet.