@MustOverride注解是什么意思?

17
在.NET中,可以向特定超类中的方法指定“mustoverride”属性,以确保子类覆盖该特定方法。我想知道是否有任何自定义Java注释能够实现同样的效果。本质上,我想要推动子类覆盖超类中的一个方法,而超类本身具有某些必须运行的逻辑。我不想使用抽象方法或接口,因为我希望在超级方法中运行一些常见功能,但是产生编译器警告/错误,表示派生类应该覆盖给定的方法。

2
如果你在Java中覆盖了一个方法,除非使用显式的“super.”,否则将不再调用超类中的方法。因此,即使存在这样的属性,我认为你的想法(强制执行公共超类方法)也不会起作用。 - Carsten
7个回答

22

我不太明白为什么你不想使用抽象修饰符——它是用于强制子类实现的,只需要在某些方法而非全部方法中使用。或者你是否在考虑C++风格的“纯虚拟”类?

但许多Java开发人员不知道的另一件事是,也可以覆盖非抽象方法并将它们声明为抽象方法,例如:

public abstract String toString(); // force re-definition

这样即使java.lang.Object已经定义了一个实现,你也可以强制子类再次定义它。


将该方法设为抽象方法。这应该是被接受的答案。 - Oliver Hausler
6
有很多时候,一个人可能希望在超级方法中有基础逻辑,但也想提醒未来的开发人员,如果不扩展此逻辑并调用super(),子类不能指望按照业务需求获得正常的行为。 - Angad
1
作为一个例子,我们大多数时候使用BaseClass。BaseClass有一个返回深拷贝的复制函数。调用ChildClass.copy()应该返回ChildClass的一个新实例,而不是基类。我希望在编译时强制执行覆盖,以避免在生产中出现细微的错误,其中1或2个字段未被复制并返回0。 - Gabe Sechan

13

忽略抽象方法,Java 中没有这样的功能。也许可以创建一个编译时注释来强制执行该行为(我并不确定),但仅此而已。

真正的难点是“覆盖超类中具有必须经过某些逻辑运行的方法”。如果你覆盖了一个方法,除非你显式地调用它,否则超类的方法不会被调用。

在这些情况下,我倾向于做如下处理:

abstract public class Worker implements Runnable {
  @Override
  public final void run() {
    beforeWork();
    doWork();
    afterWork();
  }

  protected void beforeWork() { }
  protected void afterWork() { }
  abstract protected void doWork();
}

强制规定接口方法的特定逻辑结构。例如,您可以使用它来计算调用次数,而无需担心用户是否调用super.run()等。


7

如果不能声明一个抽象基类,那么你总是可以抛出UnsupportedOperationException。

class BaseClass {
    void mustOverride() {
        throw new UnsupportedOperationException("Must implement");
    }
}

但这当然不是编译时检查...

4

我不确定你在 .NET 中考虑的是哪个属性。

在 VB 中,你可以对一个方法应用 MustOverride 修饰符,但这只相当于在 Java 中将该方法声明为抽象的。你不需要属性/注释,因为这个概念已经内置到语言中。这不仅仅是应用元数据 - 还有一个关键的区别,即抽象方法本身不包含任何实现。

如果你确实认为有这样的属性,请说明你指的是哪一个?


3

6
有用,但没有回答问题。我们需要一个注释来警告子类说“您必须重写此方法”。 - Angad

1
如果您需要一些默认行为,但由于某种原因它不应该被特殊化使用,例如在非抽象适配器类中实现逻辑以便于原型设计,但在生产中不应该使用,那么您可以封装该逻辑并记录警告,而无需实际运行它。
基类构造函数可以检查变量是否指向默认值。(我用非常抽象的术语来描述,因为我认为它应该适用于任何语言)
这将是一个(未编译、未经测试和不完整的)Java(7及以下版本)示例:
public interface SomeLogic {
 void execute();
}

public class BaseClass {

  //...private stuff and the logging framework of your preference...

  private static final SomeLogic MUST_OVERRIDE = new SomeLogic() {
    public void execute() {
      //do some default naive stuff
    }
  };

  protected SomeLogic getLogic() { return MUST_OVERRIDE; }

  //the method that probably would be marked as MustOverride if the option existed in the language, maybe with another name as this exists in VB but with the same objective as the abstract keyword in Java
  public void executeLogic() {
    getLogic().execute();
  }

  public BaseClass() {
    if (getLogic() == MUST_OVERRIDE) {
      log.warn("Using default logic for the important SomeLogic.execute method, but it is not intended for production. Please override the getLogic to return a proper implementation ASAP");
    }
  }

}

public GoodSpecialization extends BaseClass {

  public SomeLogic getLogic() {
    //returns a proper implementation to do whatever was specified for the execute method
  }

  //do some other specialized stuff...
}

public BadSpecialization extends BaseClass {

  //do lots of specialized stuff but doesn't override getLogic...
}

根据要求,有些事情可能会有所不同,尤其是对于具有lambda表达式的语言来说,显然更简单,但基本思想是相同的。

如果没有内置的东西,总有一种方法来模拟它,在这个例子中,你将在日志文件中得到一个运行时警告,使用自制的类似模式的解决方案,只有你的需求应该指出是否足够或需要更多的核心字节码操作、IDE插件开发或任何必要的巫术。


-1

我一直在思考这个问题。

虽然我不知道有什么方法可以通过编译错误来要求它,但您可以尝试编写自定义PMD规则,如果您忘记覆盖,则会引发红旗。

已经有很多PMD规则可以做一些事情,例如提醒您实现HhashCode(如果选择覆盖equals)。也许可以像那样做些事情。

我以前从未做过这件事,所以我不是写教程的人,但一个好的起点是这个链接http://techtraits.com/programming/2011/11/05/custom-pmd-rules-using-xpath/。在这个例子中,他基本上创建了一个小警告,如果您决定在导入包中使用通配符,则会出现警告。将其用作探索PMD如何分析源代码、访问层次结构的每个成员并确定您忘记实现特定方法的起点。

注解也是一种可能性,但您必须自己找出实现类路径导航的方法。我相信PMD已经处理了这个问题。此外,PMD与IDE有非常好的集成。 https://pmd.github.io/


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接