0

I was very confused when I discovered that the fields initialization in Java has some strange order. Example code when init() result is overwritten by field initialization:

public abstract class Parent { public String parentField = "dupa"; public Parent(){ init(); // uhh, bad practice to call abstract method in a super constructor } protected abstract void init(); } public class Child extends Parent { public String childField = null; // assigning null is unnecessary, another bad practice @Override protected void init(){ childField = "initialized"; System.out.println("After init(): " + childField); } } ... Child child = new Child(); // OUTPUT: After init(): initialized System.out.println("After all: " + child.childField); // OUTPUT: After all: null 

I found out what is the execution order when invoking new Child();:

  1. Parent fields initialization, but childField already exists and has default value (childField = null)
  2. Parent constructor
    • overridden init() called by parent constructor (childField = "initialized")
  3. Child fields initialization: childField = null (again)
  4. Child constructor

I know this example is full of bad practices. However, the intuitive order for me would be: fields initializations (from parents to childs), then constructors (from parents to childs).

What is the purpose of such initialization order? Why the fields initializers are not executed before their potential first use? If the field is not initialized yet, so why is it allowed to use it?

5
  • How would initialisation of of childField happen in the Child before the ctor for the Parent had finished? Commented Jan 28, 2017 at 15:10
  • The reason calling overridable methods in a constructor is bad practice is that it leaks an incompleted object to other code. Likewise, the child field initializers expect that super will be completely constructed. Commented Jan 28, 2017 at 15:36
  • If you use constructors with params in both child and parent, you would notice that the child constructor is forced to invoke the parent's before any of its own constructor code. This kind of questions seem to me bot generated Commented Jan 28, 2017 at 15:40
  • My question is not still answered: why is it allowed to use fields before initialization if it's so dangerous? If calling abstract methods in constructor is so bad practice, why not to forbid it by compiler (likewise any code before calling super() in constructor is prohibited) ? Commented Jan 28, 2017 at 21:44
  • @igrek51 because with great power comes great responsibility (to quote a famous work of literature). For example, knives are incredibly useful but nothing stops you chopping your finger off with one - nothing except training. Same with Java - you cannot expect the compiler to protect the programmer from doing stupid things. Commented Jan 29, 2017 at 20:31

1 Answer 1

3

Let me paraphrase your "intuitive" order of constructing a new object:

  1. Parent's fields are initialized
  2. Child's fields are initialized
  3. Parent's constructor is called
  4. Child's constructor is called

Well, that is not really reasonable because the initialization of child's fields can be dependent on the parent.

An object can be considered "properly initialized" when its constructor returns. Agree?

Instead of using Parent and Child, let's use Box and TreasureBox. To construct a TreasureBox, you start by making a Box. After the box is created, you add different decorations to it to make it look all fancy and cool, and you might also add a lock or whatnot.

See? the order here? It makes the most sense to properly initialize the parent first, before initializing the child, which means any initialization of the child class must occur after the parent's constructor returns. This is exactly what Java is doing.

The child's fields can be dependent on the parent's fields. To put a lock on a TreasureBox, you need to find the front of the box, and put it on there. If the box's front side has not been created yet, how can you put a lock on it?

Here's some code to clarify what I mean:

class Parent { public String parentField; public Parent(){ parentField = "Hello"; } } class Child extends Parent { public int childField = parentField.length(); } 

If Java uses your "intuitive" order, a NPE will be thrown.

Sign up to request clarification or add additional context in comments.

1 Comment

If you keep one scheme to assign only constant values in field initializers and dependent values in constructor, there is no difference between those two orders. Your example code uses opposite scheme.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.