Reputation: 731
I have been programming java professionally for more than ten years. This is one of the weirdest bugs I've ever tried to track down. I have a private member, I initialize it and then it changes to null all by itself.
public class MyObject extends MyParent
{
private SomeOtherClass member = null;
public MyObject()
{
super();
}
public void callbackFromParentInit()
{
member = new SomeOtherClass();
System.out.println("proof member initialized: " + member);
}
public SomeOtherClass getMember()
{
System.out.println("in getMember: " + member);
return member;
}
}
Output:
proof member initialized: SomeOtherClass@2a05ad6d
in getMember: null
If you run this code, obviously it will work properly. In my actual code there are only these three occurrences (five if you count the printlns) in this exact pattern.
Have I come across some bug in the JVM? Unless I'm wrong, the parent class can't interfere with a private member, and no matter what I put between the lines of code I've shown you, I can't change the value of member without using the identifier "member".
Upvotes: 3
Views: 3310
Reputation: 34460
Never, never ever call a non final method from the superclass' constructor.
It's considered bad practice, precisely because it can lead to nasty, hard-to-debug errors like the one you're suffering.
Perform initialization of a class X within X's constructor. Don't rely on java's initialization order for hierarchies. If you can't initialize the class property i.e. because it has dependencies, use either the builder or the factory pattern.
Here, the subclass is resetting the attribute member
to null
, due to superclass and subclass constructors and initializer block execution order, in which, as already mentioned, you shouldn't rely.
Please refer to this related question for concepts regarding constructors, hierarchies and implicit escaping of the this
reference.
I can only think about sticking to a (maybe incomplete) set of rules/principles to avoid this problem and others alike:
Only call private
methods from within the constructor
If you like adrenaline and want to call protected
methods from within the constructor, do it, but declare these methods as final
, so that they cannot be overriden by subclasses
Never create inner classes in the constructor, either anonymous, local, static or non-static
In the constructor, don't pass this
directly as an argument to anything
Avoid any transitive combination of the rules above, i.e. don't create an anonymous inner class in a private
or protected final
method that is invoked from within the constructor
Use the constructor to just construct an instance of the class, and let it only initialize attributes of the class, either with default values or with provided arguments
Upvotes: 0
Reputation: 206816
This happens because of the order in which member variables are initialized and constructors are called.
You are calling callbackFromParentInit()
from the constructor of the superclass MyParent
.
When this method is called, it will set member
. But after that, the subclass part of the object initialization is performed, and the initializer for member
is executed, which sets member
to null
.
See, for example:
In what order constructors are called and fields are initialized is described in paragraph 12.5 of the Java Language Specification.
Upvotes: 11
Reputation: 49181
Assignment of null
to field member
happens after executing parent constructor.
Upvotes: 3
Reputation: 50041
The fix is to change:
private SomeOtherClass member = null;
to:
private SomeOtherClass member;
Upvotes: 0