In the below code snippet, the result is really confusing.
public class TestInheritance {
public static void main(String[] args) {
new Son();
/*
Father father = new Son();
System.out.println(father); //[1]I know the result is "I'm Son" here
*/
}
}
class Father {
public String x = "Father";
@Override
public String toString() {
return "I'm Father";
}
public Father() {
System.out.println(this);//[2]It is called in Father constructor
System.out.println(this.x);
}
}
class Son extends Father {
public String x = "Son";
@Override
public String toString() {
return "I'm Son";
}
}
The result is
I'm Son
Father
Why is "this" pointing to Son in the Father constructor, but "this.x" is pointing to "x" field in Father. How is the "this" keyword working?
I know about the polymorphic concept, but won't there be different between [1] and [2]? What's going on in memory when new Son() is triggered?
Polymorphic method invocations apply only to instance methods. You can always refer to an object with a more general reference variable type ( a superclass or interface ), but at runtime, the ONLY things that are dynamically selected based on the actual object (rather than the reference type) are instance methods NOT STATIC METHODS. NOT VARIABLES. Only overridden instance methods are dynamically invoked based on the real object’s type.
So variable
x
has not polymorphic behaviour because IT WILL NOT BE SELECTED DYNAMICALLY AT RUNTIME.Explaining your code :
The Object type is
Son
sotoString()
method's OverriddenSon
version will be invoked.Object type is not in picture here,
this.x
is inFather
class sox
variable'sFather
version will be printed.See more at: Polymorphism in java
This is commonly referred to as shadowing. Note your class declarations:
and
This creates 2 distinct variables named
x
when you create an instance ofSon
. Onex
belongs to theFather
superclass, and the secondx
belongs to theSon
subclass. Based on the output, we can see that when in theFather
scope,this
accesses theFather
'sx
instance variable. So the behavior is not related to "whatthis
points to"; it's a result of how the runtime searches for instance variables. It only goes up the class hierarchy to search for variables. A class can only reference variables from itself and its parent classes; it can't access variables from its child classes directly because it doesn't know anything about its children.To obtain the polymorphic behavior you want, you should only declare
x
inFather
:and
This article discussed the behavior you're experiencing exactly: http://www.xyzws.com/Javafaq/what-is-variable-hiding-and-shadowing/15.
As other stated, you cannot override fields, you can only hide them. See JLS 8.3. Field Declarations
You can access
Father
's hidden fields fromSon
's scope usingsuper
keyword, but the opposite is impossible sinceFather
class is not aware of its subclasses.All member functions are polymorphic in Java by default. That means when you call this.toString() Java uses dynamic binding to resolve the call, calling the child version. When you access the member x, you access the member of your current scope (the father) because members are not polymorphic.
This is a behaviour done specially to have access to private members. So this.x looks at the variable X which is declared for Father, but when you pass this as a parameter to
System.out.println
in a method in Father - it looks at the method to call depending on the type of the parameter - in your case Son.So how do you call the super classes method? Using
super.toString()
, etc.From Father it cannot access the x variable of Son.
Two things are going on here, let's look at them:
First of all, you are creating two different fields. Taking a look at a (very isolated) chunks of the bytecode, you see this:
Important are lines 13, 20 and 21; the others represent the
System.out.println();
itself, or the implicitreturn;
.aload_0
loads thethis
reference,getfield
retrieves a field value from an object, in this case, fromthis
. What you see here is that the field name is qualified:Father.x
. In the one line inSon
, you can see there is a separate field. ButSon.x
is never used; onlyFather.x
is.Now, what if we remove
Son.x
and instead add this constructor:First a look at the bytecode:
Lines 4, 5 and 7 look good:
this
and"Son"
are loaded, and the field is set withputfield
. WhySon.x
? because the JVM can find the inherited field. But it's important to note that even though the field is referenced asSon.x
, the field found by the JVM is actuallyFather.x
.So does it give the right output? Unfortunately, no:
The reason is the order of statements. Lines 0 and 1 in the bytecode are the implicit
super();
call, so the order of statements is like this:Of course it's gonna print
"Father"
. To get rid of that, a few things could be done.Probably the cleanest is: don't print in the constructor! As long as the constructor hasn't finished, the object is not fully initialized. You are working on the assumption that, since the
println
s are the last statements in your constructor, your object is complete. As you have experienced, this is not true when you have subclasses, because the superclass constructor will always finish before your subclass has a chance to initialize the object.Some see this as a flaw in the concept of constructors itself; and some languages don't even use constructors in this sense. You could use an
init()
method instead. In ordinary methods, you have the advantage of polymorphism, so you can callinit()
on aFather
reference, andSon.init()
is invoked; whereas,new Father()
always creates aFather
object. (of course, in Java you still need to call the right constructor at some point).But I think what you need is something like this:
I don't have a name for it, but try it out. It will print
So what's going on here? Your topmost constructor (that of
Father
) calls aninit()
method, which is overridden in a subclass. As all constructor callsuper();
first, they are effectively executed superclass to subclass. So if the topmost constructor's first call isinit();
then all of the init happens before any constructor code. If your init method fully initializes the object, then all constructors can work with an initialized object. And sinceinit()
is polymorphic, it can even initialize the object when there are subclasses, unlike with the constructor.Note that
init()
is protected: subclasses will be able to call and override it, but classes in other package won't be able to call it. That's a slight improvement overpublic
and should be considered forx
too.