Simiil
Simiil

Reputation: 2311

Weird behaviour initializing final fields in java

I have a simple Java Program:

public abstract class Test {
  public Test() {
    foo();
  }

  public abstract void foo();
}

public class Test2 extends Test {

  private final Object testObj = new Object();

  public Test2() {
    super();
  }

  @Override
  public void foo() {
    System.out.println("object is: " + testObj);
  }

}

When instantiating Test2 its contructor calls the constructor of Test, which in turn calls the method foo implemented in Test2

during execution of foo, the field testObj is null, even though it is final.

Is this the correct behaviour? If so, is there a pattern to work around it?

Upvotes: 1

Views: 85

Answers (3)

Tony Stark
Tony Stark

Reputation: 265

Even if you don't put super() method in the constructor of base class. Compiler will place super() method itself. Eg:

class A {
    A() {
        System.out.println("A");
    }
}

class B extends A {
    B() {
        System.out.println("B");
    }
}

class C extends B {
    C() {
        System.out.println("C");
    }
    public static void main(String[] args) {
        C c = new C();
    }
}

You can recheck above function by placing super() method in first line of each constructor. Workflow will not change. There is also another rule that super() and this() method will always be placed only in first line of the constructor. This is the workflow of java in case of inheritance.

Now come to your question. When you are creating instance of Test2, super() method is called from the constructor before initialization of testobj. Hence, when foo() method is called from base class Test, testobj wast not initialized and function was called that's why it is showing null.

Upvotes: 0

M. le Rutte
M. le Rutte

Reputation: 3563

You are calling a Test2 method while Test2 hasn't finished construction yet, only the Test part of the object is finished. Therefore instance variables of Test2 are not assigned yet (thankfully they are null and not some random memory pointer !)

The 'correct' pattern would be to not call a abstract method in the base classes' constructor, and if you really need to not to have that method being dependent on its instance variables, but only on parameters.

Upvotes: 0

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726599

Yes, this is the correct behavior: testObj of the derived class gets initialized only after the superclass has finished initializing; since foo() is called before the initialization of the superclass has finished, testObj remains null.

The best work-around to this problem is to not call abstract methods inside a constructor, because they may find their object in uninitialized state. Another alternative is to postpone the call to foo(). If foo must be called as part of initialization sequence because it provides something to the base class, it should not access any state on the instance.

Upvotes: 2

Related Questions