Greg Valcourt
Greg Valcourt

Reputation: 731

How is my private member getting set to null?

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

Answers (4)

fps
fps

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

Jesper
Jesper

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

Grzegorz Żur
Grzegorz Żur

Reputation: 49181

Assignment of null to field member happens after executing parent constructor.

Upvotes: 3

Boann
Boann

Reputation: 50041

The fix is to change:

private SomeOtherClass member = null;

to:

private SomeOtherClass member;

Upvotes: 0

Related Questions