3

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".

Greg Valcourt
  • 691
  • 6
  • 13
  • 2
    Do you have a complete working example? – Sundae Mar 13 '15 at 21:03
  • 1
    What is the output of: MyObject foo = new MyObject(); foo.callbackFromParentInit(); foo.getMember(); – davidbuzatto Mar 13 '15 at 21:04
  • I can't post work code. I can tell you that the "member" identifier is used only five times in the code in the pattern as shown. There shouldn't be a way it can change to null without the identifier being used inside of this class on another line. – Greg Valcourt Mar 13 '15 at 21:07
  • You have it set to null upon class instantiation and is only inititalized in the scope of the function callbackFromParentInit(). when you call "getMember()" it returns the null value of the class, because it was never instantiated for the scope of the class. – Evan Bechtol Mar 13 '15 at 21:07
  • There's no guarantee that the "in getMember" println is from the same instance that printed "proof member initialized". – Gus Mar 13 '15 at 21:07
  • Oddly enough: MyObject foo = new MyObject(); foo.callbackFromParentInit(); foo.getMember(); - works: proof member initialized: SomeOtherClass@77f80d16 in getMember: SomeOtherClass@77f80d16 – Greg Valcourt Mar 13 '15 at 21:13

4 Answers4

11

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.

Community
  • 1
  • 1
Jesper
  • 202,709
  • 46
  • 318
  • 350
3

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

Grzegorz Żur
  • 47,257
  • 14
  • 109
  • 105
0

The fix is to change:

private SomeOtherClass member = null;

to:

private SomeOtherClass member;
Boann
  • 48,794
  • 16
  • 117
  • 146
  • That's interesting, I've obviously never done this exact pattern of inits before. It seems like a silly order. – Greg Valcourt Mar 13 '15 at 21:16
  • 2
    Ok, that fixes the immediate problem, but the real solution is to not call overridable methods from the superclass constructor. See [this question](http://stackoverflow.com/questions/3342784/using-abstract-init-function-in-abstract-classs-constructor). – Jesper Mar 13 '15 at 21:19
0

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

Community
  • 1
  • 1
fps
  • 33,623
  • 8
  • 55
  • 110