清楚表明重写方法应该调用 super 的好方法吗?

发布于 2024-12-03 08:51:50 字数 290 浏览 1 评论 0原文

这个问题困扰着我,在重写方法时很容易忘记调用 super() 。

就我而言,我正在重构一些现有的东西,其中已经有大约十个类覆盖了一个方法。直到昨天,该方法还有一个空的默认实现,因此子类是否调用 super 并不重要。 您可以发现重写器与任何值得使用的 IDE 配合得很好,但您知道它是怎么回事,电话铃声响起,同事在您背后闲聊……很容易忘记检查某个位置或以其他方式忽略它。 理想情况下,@Override 注释会有一个对应项,并且如果基类方法被注释但重写不调用 super,编译器将为这些位置生成警告。

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

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

眼泪也成诗 2024-12-10 08:51:50

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

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

    public abstract void doBarInternal();
}

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();
}
掐死时间 2024-12-10 08:51:50

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

所以你现在所拥有的是这样的:

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 设置为 Final 以避免它被覆盖,因为在这个解决方案中应该实现 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();
}    

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();
}    
流年里的时光 2024-12-10 08:51:50

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

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

无人接听 2024-12-10 08:51:50

如果您不坚持编译时安全,您可以使用一种机制,只要子类的行为不符合逻辑要求,就会抛出异常:如何强制对 super 方法进行多态调用?

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?

川水往事 2024-12-10 08:51:50

我有 2 个不同的建议:

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

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

2)需要保证对super的调用可能是设计/接口问题的指示,而不是编码/实现问题。如果你真的想保证用户调用super,最好将super类抽象化,明确指定一个抽象实现方法供他们重写,并让super控制执行流程。

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

代码重用继承总是更难管理,因此它需要仔细做。任何进行代码重用继承的人都必须对超类的内部结构有很好的了解才能正确地做到这一点(这有点令人讨厌)。例如,您是否必须在重写代码的开头或末尾调用 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.

懒猫 2024-12-10 08:51:50

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文