开发者

Good method to make it obvious that an overriden method should call super?

开发者 https://www.devze.com 2023-04-04 01:39 出处:网络
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

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 coun开发者_StackOverflowterpart 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?


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();
}


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();
}    


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


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?


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.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号