【问题标题】:Java method extension instead of overwritingJava 方法扩展而不是覆盖
【发布时间】:2013-04-15 18:14:03
【问题描述】:

在 Java 中是否有某种类型的 @annotation 或其他方法来强制方法扩展,而不是覆盖?

具体来说,假设我有一个类 Foo:

class Foo {
    public void bar(Thing thing) {
        // ...
    }
}

有没有一种方法可以在编译时强制任何扩展 Foo 并覆盖 bar 的类 X 首先调用 super.bar(thing)

【问题讨论】:

  • 您可以使用注释处理器来做到这一点。
  • @Rob 可能创建一个@Extend 注释来代替@Override

标签: java inheritance annotations


【解决方案1】:

不,你必须明确地写出来。

构造函数的旁注:实例化子类时将隐式调用超类的空构造函数,无论后者的构造函数有多少参数。

【讨论】:

  • 注意,只有在没有显式调用其他构造函数时才会隐式调用空构造函数(您可以从超类和此类中调用另一个构造函数)。
  • @AlexeiKaigorodov ...并且超类中必须有一个空构造函数(显式地,或者根本不定义构造函数),并且它不能是private
【解决方案2】:

您可以将bar 声明为final,然后从bar 调用抽象方法,这将强制子类实现“扩展”。

abstract class Foo {
    public final void bar(Thing thing) {
        barImpl(thing);
        overrideMe(thing);
    }

    private final void barImpl(Thing thing) {
        // Original implementation of "bar" here.
    }

    protected abstract void overrideMe(Thing thing);
}

编辑

我已将overrideMepublic 更改为protected,因此Foo 的用户不能只调用overrideMe 而不是bar

【讨论】:

  • 就像我在 GaborSch 的回答中指出的那样,这种方法只会稍微延迟问题。如果存在第二级继承,overrideMe 方法会遇到与原始bar 方法完全相同的问题。
【解决方案3】:

通常,您可以创建一个final 方法,该方法调用可扩展的方法。

class Foo {

    @Override
    public final void bar(Thing thing) {
        // super code comes here
        this.doBar(thing);
    }

    public abstract void doBar(Thing thing);

}

当你打电话时

foo.bar(thing);

您的超级代码首先运行,然后是子类中的代码。

这样您可以保护您的全部bar 逻辑,并且只允许扩展/重新实现某些部分。

此外,它还允许您对结果进行后处理,或将您的代码分解到某些子任务。

【讨论】:

  • 我一直使用这种模式,问题是它不适用于> 1级的继承。 doBar 方法将与原始 bar 方法具有相同的合同/问题,或者您必须启动 doDoBardoDoDoBar、... 范式。
  • @torquestomp 确实如此,最好尝试为包装逻辑找到合适的名称。实际上我从来不用多层使用它。
【解决方案4】:

虽然您不能强制代码在编译时调用其超类,但在运行时检测代码未调用超类时并不难。

class Foo {
    private boolean baseCalled;
    public final void bar(Thing thing) {
        baseCalled = false;
        barImp(thing);
        if (!baseCalled) {
            throw new RuntimeException("super.barImp() not called");
        }
    }
    protected void barImp(Thing thing) {
         baseCalled = true;
         . . . // base class implementation of bar
    }
}

请注意,这将扩展到多个继承级别,无需进一步说明。该方法特别适用于从Foo 中调用的方法;在这些情况下,您通常可以放弃final 限定符并重定向到实现方法,而只需定义基类方法来设置标志。清除标志将在每个调用点完成。

上述模式在 Android 框架中被广泛使用。它不保证super.barImp 在子类覆盖中被称为第一件事;只是它被调用了。

【讨论】:

    【解决方案5】:

    如果您使用的是 EJB,您可以尝试使用 @AroundInvoke 注释。

    通过使用反射,您可以在您的类中找到相同的方法,并且您可以使用与调用原始方法相同的参数来调用它。

    注意,在这种情况下,您必须避免调用super.bar(thing),否则它们会被调用两次。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-10-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-17
      相关资源
      最近更新 更多