Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java - (Anonymous subclass) overriding method during object instance construction

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:

  1. How is the override of the init() method working at all, during the construction of the newEntity?
  2. What is the correct terminology for this method override included in the object constructor statement?

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.

like image 897
radfast Avatar asked May 09 '17 18:05

radfast


2 Answers

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 by Super'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 the Sub constructor has a chance to initialize the date field. Note that this program observes a final field in two different states! Note also that if overrideMe had invoked any method on date, the invocation would have thrown a NullPointerException when the Super constructor invoked overrideMe. The only reason this program doesn't throw a NullPointerException as it stands is that the println 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.

like image 67
Edwin Dalorzo Avatar answered Nov 16 '22 15:11

Edwin Dalorzo


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.

like image 5
Mike Nakis Avatar answered Nov 16 '22 14:11

Mike Nakis