I am maintaining some Java 8 code which looks like this:
Class Entity {
protected Model theModel;
public Entity() {
init();
}
protected void init() {
this.theModel = new Model();
}
}
Class Model {
}
Class SubModel extends Model {
}
main {
Entity newEntity = new Entity() {
@Override
protected void init() {
this.theModel = new SubModel();
}
};
}
The code currently compiles and runs correctly, but I now need to update it.
My question are:
init()
method working at all, during the construction of the newEntity
?My research so far suggests that Java cannot dynamically override methods - cannot do overrides on this basis, because method overrides are per-class not per-object. But this code snippet seems to show that Java can do it in practice?
UPDATE: Note that the creation of the newEntity
in main
creates an anonymous sub-class, and the init()
method is being overridden for that anonymous sub-class only. This is explained better in the two excellent answers below.
As far as I can tell there is nothing special here, is just classical constructor chaining and polymorphism applied to virtual method invocations.
When you instantiate your anonymous class, it will automatically invoke its default constructor (which is automatically given by the compiler), before its default constructor succeeds it must first invoke its parent class default constructor, which in turn will invoke the init()
method, which, since it has been overridden by your anonymous class, polymorphically, ends up calling the init
method in the child class, which initializes the model to your SubModel
instance.
Joshua Bloch has a few interesting arguments against this pattern in his famous book Effective Java, in the section "Item 17: Design and document for inheritance or else prohibit" he wrote:
“There are a few more 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. To make this concrete, here's a class that violates this rule:”
He then proceeds to give an example which you would do well to study:
“Here's a subclass that overrides the
overrideMe
, method which is erroneously invoked bySuper
's sole constructor:”public class Super { // Broken - constructor invokes an overridable method public Super() { overrideMe(); } public void overrideMe() { } } public final class Sub extends Super { private final Date date; // Blank final, set by constructor Sub() { date = new Date(); } // Overriding method invoked by superclass constructor @Override public void overrideMe() { System.out.println(date); } public static void main(String[] args) { Sub sub = new Sub(); sub.overrideMe(); } }
“You might expect this program to print out the date twice, but it prints out null the first time, because the
overrideMe
method is invoked by the Super constructor before theSub
constructor has a chance to initialize the date field. Note that this program observes a final field in two different states! Note also that ifoverrideMe
had invoked any method ondate
, the invocation would have thrown aNullPointerException
when theSuper
constructor invokedoverrideMe
. The only reason this program doesn't throw aNullPointerException
as it stands is that theprintln
method has special provisions for dealing with a null argument.”
So, as you can see, and as Joshua Bloch explained so well, the risks lurk in the shadows: in the possibilities of what you can do in the overridden method, where you have license to touch instance variables that the constructor chain has not yet had a chance to initialize. The point is that you should not be allowed to touch the object state until it has been fully initialized by the constructor chain.
You might say that in your particular case that does not happen, since you are not illegally altering state and your overridden method is protected, not public, but the problem is that any person touching this code needs a very clear understanding of all these things happening under the hood, happening in places other than your current code. During maintenance it is easy to make a serious mistake, particularly when you or some other developer, comes back here to make changes, possibly months or even years after this was originally defined, and having lost context of all these dangers somebody introduces a bug that will be really hard to find and fix.
If it is in fact exactly as you are showing us, and there is no significant part of the picture missing, then the code that you have to maintain is bad, and maintenance of bad code is very troublesome.
Invoking an overridable from within a constructor is legal, but it is very bad practice, because the overridable will be invoked on a descendant whose constructor has not yet been invoked, which is catastrophic. It may not matter in trivial examples, where descendants have empty constructors, but it is bound to cause major trouble later, when things become more complicated, and a descendant suddenly one day needs to have a non-empty constructor.
And with the passage of time things do tend to become more complicated.
A halfway decent IDE would have issued a big fat warning on the invocation of the overridable from within the constructor. This in turn means that the code was written with an insufficient number of warnings enabled, which probably means that it is full of problems of this kind.
The correct terminology for this method override included in the object constructor is: Wrong.
You cannot correct this without some major refactoring. Either the model needs to be passed as a constructor parameter, or the constructor must live with the fact that the model cannot be known at all during construction.
Your question about "dynamically" overriding methods is a bit strange, and it is probably unnecessarily complicating things. Virtual method dispatching is done internally by means of a virtual method table. Each class has its own virtual method table, which never changes. However, when a constructor executes, the this
pointer points to the actual (descendant) instance, so the virtual method table in effect is that of the descendant. So, when the constructor calls an overridable, the overridable of the descendant is invoked.
That's different from C++, where the virtual method table in effect at construction time is the virtual method table of the class declaring the constructor, (irrespective of whether it has been subclassed,) so when you call a virtual method from within a C++ constructor you are not invoking any overriding methods.
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