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.
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.
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.
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.
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.
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()
...
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
andclone
. (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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With