tags:

views:

131

answers:

6

Suppose I have two classes defined the following way:

public class A {

   public A() {
      foo();
   }

   public void foo() {
      System.out.println("A");
   }
}

public class B extends A {

   private String bar;

   public B() {
      bar = "bar";
   }

   @Override
   public void foo() {
      System.out.println(bar);
   }

}

And then i instantiate B the following way:

A test = new B();

So why can't the compiler respectively the IDE warn me that there will be a NullPointer in the foo method of B? That wouldn't be to difficult to check and sometimes very useful.

+4  A: 

That's a classic gotcha. Don't use instance methods in constructors.

You may want to look at PMD, esp. the "ConstructorCallsOverridableMethod" rule.

http://pmd.sourceforge.net/rules/design.html

cadrian
A: 

In general, the compiler will not try to prove the null-ness of fields. It will do this for local variables, but there are simply too many ways to change the value of a field (i.e. serialization, reflection, or other trickery) for it to do a thorough job.

You should try to declare fields final if possible, which does help a lot for this and many other things. Alternatively, write your code to defend against nulls (i.e. "Test".equals(foo) instead of foo.equals("Test") or explicit null checks)

Steven Schlansker
+3  A: 

What goes wrong is this: you are calling the foo method in the constructor of A, but at the moment you are calling it, the object is not fully constructed yet. The constructor of B has not yet executed, so bar still has the default value null. This demonstrates why it is a bad idea to call non-final instance methods from a constructor.

The Java compiler doesn't warn for this - it warns for some constructs, but the first purpose of the compiler is to compile your code, it's not really meant to be a very sophisticated code analysis tool.

You can use a static code analysis tool like FindBugs or PMD to find problems like this in your code.

Jesper
+2  A: 

And said father Josh Bloch in his tome Effective Java:

Thou shalt not call virtual methods from a constructor in Java.

That can lead to the exact problem you are observing. At the time of calling B.foo(), B is not yet initialized, so B.bar is null.

Péter Török
+1, I like the "Thou shalt not call virtual methods from a constructor in Java".
Bakkal
+7  A: 

While this is a design error, it's not a grammatical error.

Here's some quote from Effective Java 2nd Edition, Item 17: Design and document for inheritance, or else prohibit it:

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 be 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.

An obedient compiler would let it compile just fine, since it is linguistically legal. Fortunately, code analysis tools can be used to find these design errors, e.g. findbugs:

UR: Uninitialized read of field method called from constructor of superclass (UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR)

This method is invoked in the constructor of of the superclass. At this point, the fields of the class have not yet initialized.

polygenelubricants
A: 

It's not the compiler's job to check for all kinds of "not too difficult to check" stuff that might or might not be a programming error (and there are hundreds of these).

Arguably, an IDE might mark this with a configurable warning, but again, there is too much stuff like this, and if the IDE had to check it all during on-demand compilation, it would slow down everyday work too much.

This is really the kind of thing static style checkers like FindBugs are meant for. And - surprise! - it has a check for exactly this condition:

UR: Uninitialized read of field method called from constructor of superclass

Michael Borgwardt