Java Specialists' Java Training Europehome of the java specialists' newsletter

The Java Specialists' Newsletter
Issue 2102013-05-22 Category: Language Java version: Java 1.0 - 8

GitHub Subscribe Free RSS Feed

Calling Methods from a Constructor

by Dr. Heinz M. Kabutz
Abstract:
In this newsletter we investigate what can go wrong when we call methods from constructors, showing examples from the JDK, Glassfish, Spring Framework and some other well known frameworks..

Welcome to the 210th issue of The Java(tm) Specialists' Newsletter. This is the third time that I have a special announcement to make since I started writing this newsletter in 2000. The first was in newsletter 30 and the second was in newsletter 131. We can now apply for "polyteknos" status in Greece, which used to come with all sorts of privileges, but nowadays you just get a pat on the back. Cryptic enough?

Calling Methods from a Constructor

In my opinion, we should try to only call either private or final methods from inside our constructors. The reason is that Java always calls the most derived method, which means we could call a method on a half-initialized object. Look at Newsletters 086 and 086b for a workaround involving ThreadLocal and a call to a static method. As discussed already in newsletters 062 and 062b, calling methods from an object's constructor could cause particular issues with inner classes.

In newsletter 192 and 192b, I wrote about escaping references. I also pointed out that "the anonymous class sets the this$0 field before calling super(). This is the only place where this is allowed in Java." I then asked if anyone knew the answer, but never provided a solution in one of my newsletter.

As I said in the first paragraph, we should never call overridable methods in a constructor, since the most derived class would still be busy setting up the state. This is a particularly bad code smell when the method is also abstract. I remembered that there were some notable classes in the JDK that called overridable methods from the constructor, but simply could not recall which ones they were. I was also certain that there were classes that were calling abstract methods from the constructor. How to find them?

After some attempts using awk, javap -c, and looking at BCEL, I had an idea whilst sitting at Athens airport waiting for my flight to Frankfurt to attend JAX last month. There's almost nothing like the boredom of an airport to stir the creative juices, far away from the interruptions that are so common in an office. I am a big fan of IntelliJ IDEA and use it for all my coding work. The reason I originally switched to it was that I was working on some nasty code that some programmers had whipped up in a frenzy. IDEA allowed me to quickly and easily bring some order to that code base. With IDEA's amazing code analysis tool, I was able to inspect and semi-automatically massage the code into something that I could work with. I've spoken about this experience in my talk "Productive Coder". Couldn't IntelliJ help me identify classes in the JDK where the constructor was calling abstract or overridden methods?

In the Analyzer, I enabled the following "Initialization issues" inspections:

  • Abstract method call during object construction
  • Overridden Method called during object construction
  • 'this' reference escaped in object construction
  • Overridable Method called during object construction

Calling an abstract or an overridden method in a constructor is IMHO a pretty grave offense. In most cases this points to a design flaw in your class structure. Letting "this" escape is also fairly serious and can cause some tricky initialization bugs, especially with threading. I wrote about this in newsletter 192. The least bad of these issues is the last one. I believe a well designed class should never call methods from the constructor, unless these are either private or final. But I also realise that this is not always easy to adhere to in code.

I then unzipped the src.zip file from JDK 1.7.0_21 into my current IDEA module. After running for four minutes, it came back with way over 1000 results from all sorts of packages. Fortunately, there were relatively few classes that called an abstract method from their constructor. Here are some of them:

  • com.sun.corba.se.impl.transport.CorbaConnectionCacheBase
  • com.sun.org.apache.xml.internal.security.utils.ElementProxy
  • com.sun.jmx.snmp.Enumerated
  • javax.swing.text.GapVector (non-public)
  • java.util.concurrent.LinkedBlockingDeque
  • com.sun.jmx.mbeanserver.MBeanSupport
  • com.sun.jmx.snmp.agent.SnmpTableSupport

There were over 100 classes where an overridden method was called during object construction. Included in that count were clone() and readObject(). The worst cases in my opinion were when this happened in the constructor. For example, in the javax.swing.plaf.basic.BasicComboBoxEditor, the createEditorComponent() method is invoked. In the java.util.HashMap constructors, init() is called, which is overridden by the LinkedHashMap. Fortunately the method is package access, so we cannot do real damage there.

IntelliJ IDEA trawled through 2.2m lines of code in the OpenJDK 1.7.0_21 src.zip file and found the following issues during object construction:

  • 14 x Abstract method called
  • 142 x Overridden method called
  • 252 x 'this' reference escaped
  • 961 x Overridable method called

JBoss 7.10, with its 677k lines of code showed the following object construction issues:

  • 2 x Abstract method called
  • 5 x Overridden method called
  • 117 x 'this' reference escaped
  • 58 x Overridable method called

The Spring framework, version 4.0.0, with its 743k LOC, had these issues:

  • 5 x Abstract method called
  • 14 x Overridden method called
  • 27 x 'this' reference escaped
  • 199 x Overridable method called

Since we mentioned JBoss and Spring, we should probably also mention Glassfish version 3.1, with its 1.6m LOC, had these issues:

  • 6 x Abstract method called
  • 9 x Overridden method called
  • 100 x 'this' reference escaped
  • 359 x Overridable method called

Maven, version 3.0, with only 92k LOC, had these issues:

  • 1 x Abstract method called
  • 1 x Overridden method called
  • 1 x 'this' reference escaped
  • 77 x Overridable method called

And lastly, Findbugs 2.0.2, with its 200k LOC ended up in the best shape of all:

  • 0 x Abstract method called
  • 0 x Overridden method called
  • 24 x 'this' reference escaped
  • 24 x Overridable method called

Well done to Findbugs for having the least issues!

Mixing Overridable Method Calls with Inner Classes

To see the effect of mixing overridable methods with inner classes, you will need to do some fiddling with the javac compiler. Compile the class using the following compiler flags "-target 1.3 -source 1.3". When you run it, you get a NullPointerException in the line that tests if (greeting != null). But how can that even throw a NullPointerException? Easily, since the DefaultFocusManager class calls the setDefaultFocusTraversalPolicy() method from the constructor. The test becomes if(this$0.greeting != null). In Java 1.4, the language was changed to allow the setting of this$0 before the call to super(). Thus when we compile the class with Java 1.4 or later, it prints out "hello there".

import javax.swing.*;
import java.awt.*;

public class TestFocusManager {
  public final String greeting;

  public TestFocusManager(String greeting) {
    this.greeting = greeting;
  }

  public void test() {
    DefaultFocusManager dfm = new DefaultFocusManager() {
      public void setDefaultFocusTraversalPolicy(
          FocusTraversalPolicy defaultPolicy) {
        if (greeting != null) {
          System.out.println(greeting);
        }
        super.setDefaultFocusTraversalPolicy(defaultPolicy);
      }
    };
  }

  public static void main(String[] args) {
    TestFocusManager tfm = new TestFocusManager("hello there");
    tfm.test();
  }
}
  

Kind regards

Heinz

P.S. The cryptic message at the beginning of this newsletter can be further expanded as: 3.35kg, F, 51cm, Efigenia Gabriela Kabutz.

P.P.S. I discovered another Dr Kabutz today. There are very few of us Kabutzes around, or so we thought. Turns out it is also a tribal name in Kenya, so there might be a lot more of us than we had always believed.

Language Articles Related Java Course

Java Master
Java Concurrency
Design Patterns
In-House Courses



© 2010-2014 Heinz Kabutz - All Rights Reserved Sitemap
Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners. JavaSpecialists.eu is not connected to Oracle, Inc. and is not sponsored by Oracle, Inc.
@CORE_THE_BAND #RBBJGR