是否有任何OO语言支持一种机制来确保重写的方法将调用基?


12

我认为这可能是有用的语言功能,并且想知道是否有任何语言已经支持它。

这个想法是如果您有:

class C
  virtual F
     statement1
     statement2

class D inherits C
  override F
     statement1
     statement2
     C.F()

将对CF()应用一个关键字,以便删除上面的代码的最后一行将导致编译器错误,因为它说:“可以重写此方法,但是无论如何,这里的实现都需要运行”。


Answers:


14

是的,他们有。它被称为OO的斯堪的纳维亚模型,例如在Simula中使用(另一种广泛使用的OO模型现在被认为是美国模型)。在斯堪的纳维亚模型中,您不是在压倒一切,而是在提供子行为。

在超类的方法foo中:

some-code-before
INNER // this is the actual Simula keyword
some-code-after

在子类的方法foo中:

some-code-in-subclass

如果调用父类的实例foo方法,只有some-code-beforesome-code-after情况(INNER什么都不做),但如果调用子类实例FOO,它some-code-beforesome-code-in-subclass然后some-code-after


9

我所知道的语言都没有强制调用重写方法。实际上,某些语言允许使用不可替代的替代方法(例如new在C#中使用关键字)。但是,有两种方法可以解决此问题。

第一种是创建一个不可覆盖的方法(例如,virtual在C#中缺少关键字或final在Java 中具有关键字的方法),该方法调用无法从类外部调用的可覆盖方法(例如,protected在C#,Java或C ++中)。

class C
  A
     statement1
     F
     statement3

  protected virtual F
     statement2

class D inherits C

  protected override F
     statement4
     C.F()

类覆盖C可以随意覆盖F和修改其行为,但是来自类外部的调用者只能通过访问A

编辑:正如其他人指出的那样,这称为Template方法模式

第二种方法是使用强制执行基类中指定的前提条件和后置条件的语言,例如带有代码协定的Eiffel或C#。它不会强制调用基类,但是可以强制重写方法执行相同的语句。如果语言允许继承方面,则使用方面也可能会有所帮助。


2
您甚至可以使该方法private在C ++中被覆盖:) Herb Sutter 在此详细说明。
fredoverflow 2012年

模板模式只有一个缺点,即您在遍历继承层次结构时每次都需要重新实现它。Simula的示例更加优雅,并且仍然允许模板模式。
Pavel Voronin

7

这并不是语言的真正组成部分,但是Java的FindBugs静态代码分析器具有注释OverrideMustInvoke,开发人员可以将其添加到方法中,并且如果发现未调用超级实现的重写方法,这将导致FindBugs显示错误。 。它甚至允许在覆盖方法中指定调用必须是第一个还是最后一个。


6

要求调用超类方法是反模式。如果在编译时未强制执行,则容易出错,这就是为什么您要寻找一种语言结构来对其进行检查的原因。

所有OO语言都支持一种方法:模板方法pattern。在这里,使超类方法不可重写,并在其中调用可重写方法。然后,子类可以重写此方法以添加功能:

class super {
  public final void doSomething() {
    doSpecialthing();
    doMore();
  }
  public void doSpecialthing() {
  }
}

根据对重写方法的调用位置,它甚至还可以确定执行顺序,对于普通的super调用,子类实现者可以随意执行。


1

我能想到的最接近的模式是自我订阅的事件。这有点麻烦,对于编码人员来说一点也不直观,但是可以达到目标。

class C
{
    public void F()
    {
        ...
        OnF()
    }

    protected event OnF
}

class D : C
{
    public D()
    {
        base.OnF += this.F
    }

    private void F
    {
        ...
    }
}

1
这是一个相当常见的设计模式,有时会同时使用前后覆盖。ViewWillAppear(),ViewDidAppear()。当您想允许子类扩展(而不是修改)默认行为时,这是理想的选择。
克里斯·范拜尔

1

Lisp机器“味道”允许使用继承的main方法类型为“ before”,“ after”和“ around”的方法。


0

从理论上讲,这虽然不是一个坏主意,但它的负面影响确实是在实施时限制了我的选择D。例如,如果(出于某些不可思议的原因),F从其他方法调用超类实现会更方便:

class D inherits C
    override F
        statement1
        statement2
        G()
    G
        statement3
        C.F()
        statement4

在您的情况下,我想象编译器会标记Fin 的实现D,即使它(间接)调用C.F()

基本上,您所描述的是一种可能的机制,可以帮助编译器识别何时C违反了从其继承的类的协定。我的观点是,虽然这是一个伟大的事情,它不应以限制的代价如何,我可以实现我的子类。


1
-1:能够想到您不想使用的情况并不能解决“任何语言都可以做到这一点”的问题。

@GrahamLee:非常正确。我的观点是尝试解释为什么没有一种语言(我知道)实现这种功能的原因。我想我很被解释着迷住了,以至于我忘了提到我为什么要解释它了。-1愉快地接受了。:)
Mac
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.