I have this simple piece of code.
abstract class X {
X() {
read();
}
private void read() {
Object obj = new Object();
readValue(obj);
}
protected abstract void readValue(Object obj);
}
class Y extends X {
Object obj = null;
Y() {
super();
}
@Override
protected void readValue(Object obj) {
this.obj = obj;
}
void printer() {
System.out.println("Object = " + obj);
}
}
class Runner {
public static void main(String[] args) {
Y y = new Y();
y.printer();
}
}
When I run the above code, the object gets printed as null. (I get "Object = null")
Surprisingly, in class Y when I remove null declaration
Object obj;
The actual value of the object is printed.
Something like ("Object = java.lang.Object@3cd1a2f1")
Why is such a behavior observed? What is 'this' pointing to? Any object is initialized by null if we just declare it, then why such an aberrant behavior?
The reason the
obj
field is null is due to the sequence of steps that occur in the constructor call ofY
:Y
calls the super constructor which eventually callsreadValue
of the concrete classY
, therefore assigning a non-null value to theobj
field.After the super constructor finishes, the instance field
obj
is initialized to null due to the variable initializer:When you remove the
null
initializer, it becomes a simple field declaration with no instance initialization to be performed in step 2.The apt solution is not to remove the
null
initializer, but to re-design the whole class hierarchy. For example, since the purpose ofreadValue
seems to just be a setter for a variable, then you don't need to make it override an abstract method in the parent class. Just set it as a separate method and call it after the constructor ofY
completes.The object is null because The superclass constructor runs before the subclass constructor,hence it would only be natural that the statement Object obj = null; is executed after calling super class constructor.
The assignment of Object obj = null; is inlined into the constructor during compile time. This are only accessible in existing instance, and instance does not exist yet when you are in constructor (it is still under construction).
You can achieve object value(Object = java.lang.Object@3cd1a2f1) by declaringobject object as static.
static Object obj = null;
Generally though, it's bad practice to call overriden methods from a constructor in real time.
This illustrates the dangers of calling an inherited method in a subclass from a superclass constructor. The main danger is that initializers for variables in a subclass run after the superclass constructor completes.
Here is what happens.
y
is created.X()
is called, which callsread()
.read
method creates a newObject
and passes it toreadValue
, which is implemented inY
.readValue
method inY
setsobj
to the new object.X()
completes, and initializers run now inY
, settingobj
tonull
.printer
method prints"Object = null"
.If you remove the declaration of
obj
inY
, then there is no initializer to run, and theobj
variable retains its value.The JLS, Section 12.5, states:
(emphasis mine)
and