明确被覆盖的方法应该调用super的好方法?

时间:2011-09-08 15:09:02

标签: java annotations override super

这个问题只是让我感到困惑,在重写方法时很容易忘记调用super()。

在我的情况下,我正在重构一些现有的东西,其中已经有大约十个类覆盖了一个方法。直到昨天,该方法都有一个空的默认实现,所以如果子类称为super,则无关紧要。 你可以在任何有价值的IDE中找到覆盖物,但是你知道它是怎么回事,电话铃声,同事们背后都有一个小小的东西......很容易忘记检查一个地方或者忽略它。 理想情况下,@ Override注释会有一个副本,如果基类方法被注释但是覆盖不会调用super,编译器会为这些位置生成警告。

我能做的最好的事情是什么?

6 个答案:

答案 0 :(得分:10)

不太优雅,但可能的解决办法是将该方法分解为两个:

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

    public abstract void doBarInternal();
}

答案 1 :(得分:3)

如果必须始终调用超类实现,则可以使用“模板方法”模式。

所以你现在拥有的是:

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

这将成为:

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

(为了在一个班级内轻松测试,课程是静态的)

我做了最后的doSomething以避免被覆盖,因为在这个解决方案中应该实现childDoSomething。

当然这个解决方案意味着Parent不能再被用作具体的类。

编辑:阅读有关Child实施第三方界面的评论后;这不一定是个问题:

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

答案 2 :(得分:2)

为了其他东西,我在FindBugs中找到了有趣的 OverrideMustInvoke注释http://findbugs.sourceforge.net/api/edu/umd/cs/findbugs/annotations/OverrideMustInvoke.html

答案 3 :(得分:1)

如果您不坚持编译时安全性,那么只要子类的行为不符合逻辑要求,就可以使用一种抛出异常的机制:How do I force a polymorphic call to the super method?

答案 4 :(得分:1)

我有两个不同的建议:

1)构建一个junit测试,发现你的基类的所有子类,然后选择用@Override注释装饰的所有方法。我有一些类似的单元测试(例如,查找所有子类,检查它们真正的是否可以序列化)。

不幸的是,验证他们是否称之为“超级”并不那么直截了当。您可能需要让测试查找源文件,并搜索它,或者更好(但我不知道如何执行此操作),读取字节代码并查看是否可以检测到对super的调用。

2)需要保证对 super 的调用可能是设计/接口问题的指标,而不是编码/实现问题。如果你真的想要保证用户调用super,那么最好使超类 abstract ,明确指定一个抽象实现方法让它们覆盖,并让超级控制执行流程。

如果要定义默认实现,以便并非所有用户都需要子类提供实现该方法,则可以为人们定义一个默认实现类供人们使用。 (如果你真的想控制它,那么将默认的类实现方法定义为 final ,以强制它们返回子类化抽象父类。)

代码重用继承总是难以管理,因此需要仔细完成。任何进行代码重用继承的人都必须对超类的内部有一个很好的了解才能做到正确(这有点令人讨厌)。例如,您是否必须在覆盖代码的开头或最后调用super.method()? (或者你可以在中间做......)

总而言之,最好的解决方案是尝试避免必须强制执行的设计。

答案 5 :(得分:0)

什么可行 - 创建一些标记注释(即@MustCallParent),然后创建一些注释处理器来检查标记它的方法是否符合约束。