Sebastian Höffner
Sebastian Höffner

Reputation: 1944

Order of initialization with inherited and super classes

Question: Am I right that the super() constructor is called before the field initializations of my inherited class are done and by this the initializations can overwrite what I already initialized?

The title and maybe the question sound somewhat confusing, so I will try to clarify my question.

I have an abstract class Geometry which has a protected abstract void method. This method is called in the constructor of the Geometry. I figured out that this call caused the problem I had.

In my extended class, ExtendedGeometry I implemented the abstract method by calling a private method:

@Override
protected void createGeometry() {
    loadModel();
}

The private void loadModel() method filles a vertexList and is done.

The vertexList is defined as private ArrayList<Integer> vertexList = new ArrayList<Integer>();.

When I now call update() on my model, the vertexList appears to be empty. And even before that, although I initialized the variable directly as shown above, it appears to be null in the loadModel() method.

So I think my steps are:

  1. I call the (implicit) constructor of ExtendedGeometry.
  2. I implicitly initialize a private member variable.
  3. The super constructor calls my overriden method which calls loadModel().
  4. -> Odd: The member variable is now null, but declared!
  5. I initialize the member variable again.
  6. I fill it with values.
  7. During the main loop I print the contents of the variable, but it's empty.

So in step 4 I expected the variable to be not null and in step 7 to be filled.

To solve it I found three solutions:

So after trying out all this stuff I realized that the explanation seems to be that my superclass is able to work with the declaration but not the initialization of my subclass. This leads me to the conclusions that the steps done are:

  1. The declarations are promoted. (during compile time)
  2. I call the (implicit) constructor of ExtendedGeometry.
  3. The super() constructor is called.
  4. The members of ExtendedGeometry get initialized. (What overrides the initialization done with super().)

So my question is a simple yes/no question (which could be extended with explanations, if I'm wrong - or additions if I'm right):

Am I right that the super() constructor is called before the field initializations of my inherited class are done and by this the initializations can overwrite what I already initialized?

I first thought this is a problem of visibility, but while tracking down the problem this seems to be the only solution I can come up with.

Just for completeness and maybe to better understand here is my 95 % reduced code, sorry it has no JavaDoc. But I added some comments to show the problem and explain.

The most important one, here is the ExtendedGeometry.java: package geometry;

import java.util.ArrayList;

public class ExtendedGeometry extends Geometry {
    // PROBLEM 1: the ArrayList is already initialized here
    private ArrayList<Integer> vertexList = new ArrayList<Integer>();
                     // usually this is Vector3f from lwjgl, but for
                     // STO I changed it to Integer

    private void loadModel() {
        // PROBLEM 2: but the ArrayList is null here!
        System.out.println("Initializing vertexList which is " + vertexList);

        // PROBLEM 3: leaving the following lines out
        //            does not change anything in the output
        //            of the update() method
        if(vertexList == null)
            vertexList = new ArrayList<Integer>();

        // PROBLEM 4: filling the "newly" initialized vertexList, 
        //            but not the old one
        vertexList.add(1);
        vertexList.add(2);
        vertexList.add(3);
    }

    public void update() {
        // PROBLEM 5: as you can see, you see nothing: 
        //            the vertexList has no content
        System.out.println("vertexList of size: " + vertexList.size());
        for(Integer i : vertexList) {
            System.out.print(i);
        }
        System.out.println();
    }

    /*// PROBLEM 6 / SOLUTION: If I leave out the loadModel in 
      // createGeometry() but use this constructor instead
      // it works
    public SpecializedGeometry() {
        loadModel();
    }
    */

    @Override
    protected void createGeometry() {
        loadModel();
    }
}

The Geometry.java class, its super class:

package geometry;

public abstract class Geometry {
    public Geometry() {
        // every geometry has to be created
        createGeometry();
    }
    /*// If I add an init method like this, it works
    public void init() {
        createGeometry(); 
        // of course this line has to be removed
        // from the constructor
    }
    */

    // this is the abstract method to create a geometry
    protected abstract void createGeometry();
}

The Simulation.java just demonstrates how my program is build in general:

package simulation;

import geometry.ExtendedGeometry;

public class Simulation {
    private ExtendedGeometry geometry = null;

    public Simulation() {
    }

    public boolean init() {
        // initializes all simulation stuff, 
        // now only creates the geometry
        geometry  = new ExtendedGeometry();
//          geometry.init(); // this is a possible fix, see Geometry.java
        return true;
    }

    public void update() {
        // does calculations and updates everything
        // accordingly
        if(geometry != null) {
            geometry.update();
        }
    }
}

The MainWindow.java, nothing too special here:

package main;

import simulation.Simulation;

public class MainWindow {
    private Simulation simulation = null;

    public boolean init() {
        // create the simulation and initialize it
        // usually here is a bunch of initializations
        simulation = new Simulation();
        return simulation.init();
    }

    public void run() {
        // in this example I close after 10 loop runs, 
        // of course 1 would be sufficient as well
        int x = 0;

        // the main loop handles inputs, event manager,
        // updates, drawing, ...
        while(x++ < 10) {
            // update simulation
            simulation.update();
        }
    }
}

And finally the boring Main.java:

package main;

public class Main {
    public static void main(String[] args) {
        MainWindow mw = new MainWindow();
        if(mw.init()) { // init and
            mw.run();   // run main loop
        }
    }
}

Upvotes: 2

Views: 1164

Answers (1)

John B
John B

Reputation: 32959

I believe you have answered your own question that Yes, the extended class is fully initialized before the child class is initialized.

You have run into a basic rule of OOD, you should "never" call a non-static, non-final method from a constructor. If the constructor calls a method that can be overridden by a sub-class errors of this type are too common.

Upvotes: 1

Related Questions