Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way to initialize member variables of a subclass in Java before the superclass' constructor is called?

Tags:

java

oop

I need this because the constructor in the superclass is calling a method which is overridden in the subclass. The method returns a value which is passed to the subclass' constructor. But the superclass constructor must be called before the subclass constructor, so I have no chance to save the value passed in.

like image 363
Attila Kun Avatar asked Mar 27 '10 14:03

Attila Kun


People also ask

Can constructors be inherited through a subclass that calls the superclass constructor?

Constructors are not inherited. The superclass constructor can be called from the first line of a subclass constructor by using the keyword super and passing appropriate parameters to set the private instance variables of the superclass.

Which constructor is called first that of the subclass or the superclass?

The answer is that in a class hierarchy, constructors are called in order of derivation, from superclass to subclass. Further, since super( ) must be the first statement executed in a subclass' constructor, this order is the same whether or not super( ) is used.

Can we have a variable in a subclass with a same name as in the superclass?

If your subclass defines a member variable with the same name as a member variable in its superclass, the variable in the subclass hides the one in the superclass. Thus, the subclass does not inherit the variable from its superclass.

Why call super must be first in constructor?

Call to super() must be the first statement in the Derived(Student) Class constructor because if you think about it, it makes sense that the superclass has no knowledge of any subclass, so any initialization it needs to perform is separate from and possibly prerequisite to any initialization performed by the subclass.


2 Answers

Calling an overridden method from the superclass constructor is simply not going to work - don't do it. The superclass constructor must always finish before that of the subclass. While the superclass constructor is executing, the object in question is a (half initialized) instance of the superclass, not the subclass! So if you try to call any overridden function from the constructor, the subclass fields it may depend on are not yet initialized (just as you have observed). This is a fundamental fact of class design, and there is no workaround.

As explained in Effective Java 2nd. Ed. (Chapter 4, Item 17):

There are [...] restrictions that a class must obey to allow inheritance. Constructors must not invoke overridable methods, directly or indirectly. If you violate this rule, program failure will result. The superclass constructor runs before the subclass constructor, so the overriding method in the subclass will get invoked before the subclass constructor has run. If the overriding method depends on any initialization performed by the subclass constructor, the method will not behave as expected.

If you can change the superclass implementation, try moving the call to the virtual function out of the constructor. One way to achieve this is using a Factory Method:

class Super {
    public void init() { ... }
}

class Subclass extends Super {
    private Subclass() { super(); ... }
    public void init() { super.init(); ... }
    public static Subclass createInstance() {
        Subclass instance = new Subclass();
        instance.init();
        return instance;
    }
}

Note that the constructor of Subclass is private to ensure that it can only be instantiated via createInstance(), thus instances are always initialized properly. OTOH this also prevents further subclassing. However, subclassing a concrete class is not recommended anyway - a class meant to be subclassed should be abstract (with a protected constructor in this case). And of course, any further subclasses must also have non-public constructors and static factory methods which diligently call init()...

like image 74
Péter Török Avatar answered Sep 30 '22 14:09

Péter Török


Your question is summarized by the following snippet:

abstract class Base {
    Base() {
        dontdoit();
    }
    abstract void dontdoit();
}
public class Child extends Base {
    final int x;
    Child(int x) {
        this.x = x;
    }
    @Override void dontdoit() {
        System.out.println(x);
    }

    public static void main(String args[]) {
        new Child(42); // prints "0" instead of "42"!!!
    }
}

This is a quote from Josh Bloch and Neal Gafter's Java Puzzlers: Traps, Pitfalls, and Corner Cases:

The problem arises whenever a constructor calls a method that has been overridden in its subclass. A method invoked in this way always runs before the instance has been initialized, when its declared fields still have their default values. To avoid this problem, never call overridable methods from constructors, either directly or indirectly [EJ Item 15]. This prohibition extends to instance initializers and the bodies of the pseudoconstructors readObject and clone. (These methods are called pseudoconstructors because they create objects without invoking a constructor.)

In short: DON'T DO IT!

Moreover, the book proposes a possible solution (slightly edited for generality):

You can fix the problem by initializing the field lazily, when it is first used, rather than eagerly, when the instance is created.

like image 25
polygenelubricants Avatar answered Sep 30 '22 12:09

polygenelubricants