danieljimenez
danieljimenez

Reputation: 1430

NullPointerException with an override method

Can anyone comment with their opinion on the best way to resolve the following issue? I've tried init blocks and initializing the field in the constructor, but neither get called before the overridden method tries to clear it.

 class TestRunner {
        public static void main(String[] args) {
            ftw ftw = new ftw();
            ftw.dontPanic();
        }
    }

    class wtf {

        wtf(){
            this.dontPanic();
        }

        void dontPanic() {
            System.out.println("super don't panic");
        }
    }

    class ftw extends wtf {
        final HashSet variableOfDoom = new HashSet();

        ftw(){
            super();
        }

        void dontPanic() {
            super.dontPanic();
            System.out.println("sub don't panic");
            variableOfDoom.clear(); //This line prints a null pointer exception, because the field hasn't been intialized yet.
        }
    }

Upvotes: 2

Views: 2710

Answers (7)

user85421
user85421

Reputation: 29710

quick & dirty solution (not very nice, I know)

...
System.out.println("sub don't panic");
// must test if object fully initialized, method called in constructor
if (variableOfDoom != null) {
    variableOfDoom.clear();
}

Upvotes: 0

Tom Anderson
Tom Anderson

Reputation: 47243

Firstly, don't do that.

Secondly, if you absolutely have to do that, do this:

class wtf {

    wtf(){
            this.dontPanic();
    }

    void dontPanic() {
            System.out.println("super don't panic");
    }
}

class ftw extends wtf {
    HashSet _variableOfDoom; // underscore to remind you not to access it directly

    ftw(){
            super();
    }

    private HashSet getVariableOfDoom()
    {
        if (_variableOfDoom == null) _variableOfDoom = new HashSet();
        return _variableOfDoom;
    }

    void dontPanic() {
            super.dontPanic();
            System.out.println("sub don't panic");
            getVariableOfDoom().clear(); // FOR GREAT JUSTICE!
    }
}

Note that you can't have variableOfDoom be final.

Upvotes: 1

neesh
neesh

Reputation: 5275

The takeaway from this example is: super class initialization cannot depend on the child class being completely initialized.

If ftw did not extend wtf then it would be ok to assume that any initialization specified at the field definition will be done before the constructor is called. However, since ftw extends wtf, wtf must be fully initialized before any initialization can happen in ftw. Since part of wtf initialization depends on variableOfDoom in the sub class to have been initialized you are getting a null pointer exception.

The only way out of this is for you to separate your call to dontPanic outside the constructor.

Upvotes: 2

devgeezer
devgeezer

Reputation: 4189

You might note that private methods are not prevented from calling overrides; so even then, exercise some caution in calling anything that is overridden from constructors. In other languages the advice is to avoid calling any virtual methods from a constructor because the object may not be fully constructed when the method is called.

An alternative to consider is two-stage construction. http://www.artima.com/forums/flat.jsp?forum=106&thread=106524

There's a related idiom in the .NET Framework Design Guidelines book called "create-set-call". Create the object, set some properties, call some methods. I've seen this usage sometimes criticized as being less discoverable than the simple construction call (i.e., just create-and-use).

Upvotes: 1

Bert F
Bert F

Reputation: 87593

Don't call non-final methods from within constructors - not all instance variables may be initialized yet - your code being a classic example.

Without know what you're really trying to accomplish, its difficult to recommand "the best way to resolve the issue", but here's a go:

class TestRunner {
    public static void main(String[] args) {
            ftw ftw = new ftw();
            ftw.dontPanic();
    }
}

class wtf {

    wtf(){
            wtfDontPanic();
    }

    private final void wtfDontPanic() {
            System.out.println("super don't panic");
    }

    void dontPanic() {
            wtfDontPanic();
    }
}

class ftw extends wtf {
    final HashSet variableOfDoom = new HashSet();

    ftw(){
            super();
            ftwDontPanic();
    }

    private final ftwDontPanic() {
            System.out.println("sub don't panic");
            variableOfDoom.clear(); //This line prints a null pointer exception, because the field hasn't been intialized yet.
    }

    private void dontPanic() {
            super.DontPanic();
            ftwDontPanic();
    }
}

Upvotes: 0

Hank Gay
Hank Gay

Reputation: 72019

This is why they recommend you don't call non-final methods from inside a constructor. private methods are also safe, since they can't be overriden.

Assuming that you can't follow that sound advice, for some reason, here are some other observations that might help:

  • I'm assuming you simplified for the sake of the example, but the call to .clear() doesn't really accomplish anything in the example; you may be able to just delete it.
  • It's tough to say without seeing the actual program in total, but you may be able to skate by with assigning the variable a new HashSet instead of invoking .clear().

Upvotes: 7

Michael Myers
Michael Myers

Reputation: 192025

Here's the problem: Your execution flow looks like this:

main
->ftw()
  ->wtf()
    ->wtf.dontPanic()

But since method invocations are determined at runtime in Java, ftw.dontPanic() is the method that's really being called. And variableOfDoom won't be set until the constructor completes, but the constructor will never complete because of the exception.

The solution is not to do work in your constructor (at least not with non-private non-final methods).

Upvotes: 7

Related Questions