2

This problem just bit me, its pretty easy to forget to call super() when overriding a method.

In my case I was refactoring some existing stuff where there were already about ten classes overriding a method. Until yesterday the method had an empty default implementation, so it didn't matter if subclasses called super or not. You can find the overriders just fine with any IDE worth its salt, but you know how it is, phone rings, colleagues have a smalltalk behind you back... its easy to forget to check a place or otherwise overlook it. Ideally there would be a counterpart for the @Override annotation and the compiler would generate a warning for those places if the base class method is annotated but the override doesnt call super.

Whats the next best thing I can do?

Durandal
  • 19,919
  • 4
  • 36
  • 70

6 Answers6

10

Not quite elegant, but possible solution is to break that method into two:

public abstract class Foo {
    public void doBar() {
        // do your super logic
        doBarInternal();
    }

    public abstract void doBarInternal();
}
axtavt
  • 239,438
  • 41
  • 511
  • 482
  • 5
    I'd say it's reasonably elegant - this is the template method pattern, basically. – Jon Skeet Sep 08 '11 at 15:13
  • Not really an option for ensuring already existing code really calls super, now is it? This may be nice in theory, but reality bites back ugly: you can't refactor 3rd party stuff. – Durandal Sep 08 '11 at 15:22
  • @Durandal since you mention _3rd party stuff_ blocking refactoring, [anticorruption layer](http://stackoverflow.com/questions/909264/ddd-anti-corruption-layer-how-to) naturally comes to mind. Within such a layer, one can safely encapsulate whatever checks and patches are needed and keep own code clean – gnat Sep 08 '11 at 15:45
  • 1
    @Durandal - but you actually gave an example in the question telling about adding an annotation to the base class. How add the annotation to 3rd party class? Thinking about that... maybe the answer is: add an delegate class which incorporates above solution and only allow that class to be used. – user85421 Sep 08 '11 at 15:45
  • In my specfic case, indeed the base class is my source. But the method where I forgot to call super is the implementation of an interface, which is not my source, and neither is the code calling said interface. But thats beside the point, I'm looking for a reasonably quick and simple solution. Refactoring a dozen classes to avoid making ONE kind of mistake while opening the opportunity for a bunch of new mistakes isn't my kind of cure for the problem. – Durandal Sep 08 '11 at 16:15
  • @Durandal: please look at my revised answer, I think you can still use the template method approach in the situation you describe. – Adriaan Koster Sep 08 '11 at 16:33
3

If the superclass implementation MUST ALWAYS be called you could use the 'template method' pattern.

So what you have now is something like:

public static class Parent {
    public void doSomething() {
        System.out.println("Parent doing something");
    }
}

public static class Child extends Parent {
    public void doSomething() {
        // MUST NOT FORGET SUPER CALL
        super.doSomething();
        System.out.println("Child doing something");
    }
}

public static void main(String[] args) {
    Child child = new Child();
    child.doSomething();
}

And this would become:

public abstract static class Parent {
    public final void doSomething() {
        System.out.println("Parent doing something");
        childDoSomething();
    }

    public abstract void childDoSomething();
}

public static class Child extends Parent {
    public void childDoSomething() {
        System.out.println("Child doing something");
    }
}

public static void main(String[] args) {
    Child child = new Child();
    child.doSomething();
}

(classes are made static for easy testing within one class)

I made doSomething final to avoid it being overridden since in this solution childDoSomething should be implemented instead.

Of course this solution means Parent can no longer be used as a concrete class.

EDIT: after reading comments about Child implementing a third party interface; this does not need to be a problem:

public interface ThirdPartyInterface {
    public void doSomething();
}

public abstract static class Parent {
    public final void doSomething() {
        System.out.println("Parent doing something");
        childDoSomething();
    }

    public abstract void childDoSomething();
}

public static class Child extends Parent implements ThirdPartyInterface{
    public void childDoSomething() {
        System.out.println("Child doing something");
    }

//    public final void doSomething() {
//        // cannot do this because Parent makes it final
//    }
}

public static void main(String[] args) {
    Child child = new Child();
    child.doSomething();
}    
Adriaan Koster
  • 15,870
  • 5
  • 45
  • 60
2

Loooking for something else I found interesting OverrideMustInvoke annotation in FindBugs: http://findbugs.sourceforge.net/api/edu/umd/cs/findbugs/annotations/OverrideMustInvoke.html

Rostislav Matl
  • 4,294
  • 4
  • 29
  • 53
1

If you do not insist on compile time safety you could use a mechanism that throws an exception whenever a subclass does not behave as logically required: How do I force a polymorphic call to the super method?

Community
  • 1
  • 1
dcn
  • 4,389
  • 2
  • 30
  • 39
1

I have 2 different suggestions:

1) Build a junit test that discovers all subclasses of your base class, and then selects all the methods decorated with the @Override annotations. I have some unit tests that do something similar (i.e. find all subclasses, check that they truly are Serializable for example).

Unfortunately, the verification of whether they call "super" is a little less straightforward. You would either need to have the test look up the source file, and search it, or better (but I don't know how to do this), read the byte code and see if you can detect the call to super.

2) Needing to guarantee a call to super is probably an indicator of a design/interface issue, rather than a coding/implementation issue. If you really want to guarantee that users call super, it would be better to make the super class abstract, clearly designate an abstract implementation method for them to override, and have the super control the flow of execution.

If you want to define a default implementation, so that not all users need to subclass provide implement that method, you could define a default implementation class for people to use. (And if you really want to control it, define that default class implementation method as final to force them to go back to subclassing the abstract parent.)

Code reuse inheritance is always harder to manage, and so it needs to be done carefully. Anyone doing code reuse inheritance has to have a good idea of the internals of the super class to do it right (which is a bit yucky). For example, do you have to call super.method() at the beginning of your overriding code, or at the end? (or could you do it in the middle...)

So in summary, the best solution is to try avoid a design where you have to enforce that.

Sam Goldberg
  • 6,711
  • 8
  • 52
  • 85
0

What could work - creating some marker annotation (i.e. @MustCallParent) and then create some annotation processor to check that methods marked with it comply to the constraint.

Rostislav Matl
  • 4,294
  • 4
  • 29
  • 53
  • It's the other way around: @SubclassesMustCallSuper annotation on the parent method. – toto2 Sep 08 '11 at 15:36
  • I don't know much about annotations, but I doubt they can check that super.method() has been called within an overriding method. – toto2 Sep 08 '11 at 15:37
  • I don't see why it would be a problem to collect this way a list of files with classes marked this way and to do more or less sophisticated code analysis. – Rostislav Matl Sep 08 '11 at 16:01
  • @toto2 an annotation processor (in jdk7+8) has access to the whole AST tree of the java compilation process, and therefore can know if super.method() was called. – aepurniet Sep 09 '13 at 20:02