【问题标题】:Make sure method is executed, even if overriden确保方法被执行,即使被覆盖
【发布时间】:2013-12-24 10:26:26
【问题描述】:

假设我有一个包含一些方法的接口,如下所示:

interface Task {
    void before();
    void doStuff();
    void after();
}

这里我会实现它的一部分:

abstract class PoliteTask implements Task{

    @Override
    public void before() {
        System.out.println("Hey");
    }


    @Override
    public abstract void doStuff();


    @Override
    public void after() {
        System.out.println("Cya");
    }

}

现在我想确保在所有扩展类中调用那些 before()after() 实现。

这里我们有一个类需要在before()中初始化一些东西:

class ActualStuffTask extends PoliteTask {

    private int fancyNumber;


    @Override
    public void before() {
        // init some things
        fancyNumber = 42;
    }


    @Override
    public void doStuff() {
        System.out.println("Look, a number: "+fancyNumber);
    }

}

显然,ActualStuffTask 会覆盖 before(),因此它不会说“嘿”,而只会说“Cya”。

如果我在PoliteTaskfinal 中创建方法,则不会发生这种情况,但是它的子类无法覆盖这些方法。

ActualStuffTask 中调用super.before() 会起作用,但我希望保证这种效果,无论子类实现如何。

问题是:

我应该使用什么模式来同时拥有父实现和子实现?

【问题讨论】:

  • 您是否考虑过在子实现中只使用super.before()
  • @JeroenVannevel API不能依赖children的实现。
  • 那么你将不得不放弃基类中的接口并以不同的方式命名方法。您可以只实现 abstract 并从基类中的(不同名称的)调用它们。

标签: java class oop hierarchy


【解决方案1】:

我喜欢使用您在实现类中实现的抽象方法。

abstract class PoliteTask implements Task{

    @Override
    public final void before() {
        System.out.println("Hey");
        doBefore();
    }

    protected abstract void doBefore();
    protected abstract void doAfter();

    @Override
    public abstract void doStuff();


    @Override
    public final void after() {
        System.out.println("Cya");
        doAfter();
    }

}

class ActualStuffTask extends PoliteTask {
    private int fancyNumber;

    @Override
    protected void doBefore() {
        // init some things
        fancyNumber = 42;
    }


    @Override
    public void doStuff() {
        System.out.println("Look, a number: "+fancyNumber);
    }


    @Override
    protected void doAfter() {
        // something else
    }
}

注意Task 方法是final。他们不需要。这取决于您构建 API 的方式。

【讨论】:

  • 是的,我也用这个。我只是想找到一个更清洁、通用的解决方案——阅读:不改变抽象签名。
  • @MightyPork 您不必更改签名。 PoliteTask 的子类已经实现了。
  • 我想这是真正的答案。我想为这些方法保留相同的名称,但显然我不能拥有它。
  • @MightyPork 您确实保留了相同的方法名称。由于ActualStuffTaskPoliteTask,因此您可以在ActualStuffTask 类型的变量上调用before()。该调用将通过在PoliteTask 中实现的before() 并调用在ActualStuffTask 中实现的doBefore()
  • 是的,我知道,我的意思是那些你可以在子类中实现的。以继承 JavaDoc 为例。
【解决方案2】:

这种情况的常用方法是这样的(简化示例):

abstract class Base {

    public final void before() {
        System.out.println("Hey");
        doBefore();
    }

    protected void doBefore() {
    }
}

这样基本代码总是会被执行,子类可以添加它们的实现。

【讨论】:

    【解决方案3】:

    您可以关注template method pattern。在AbstractClass(比如doAll)中创建一个final 方法,按顺序调用其他方法:

    public final void doAll() {
        before();
        doStuff();
        after();
    }
    

    那么你可以让beforeafter 也可以是final 方法,这样它们将始终由子类执行,并且它们的行为不能改变。

    【讨论】:

    • 这看起来是个有趣的模式,但是doAll() 是怎么调用的呢?
    • 这个doAll() 方法将作为其API 的一部分被调用。通常你会通过设置其他方法protected 来强制执行它,强制用户只调用public 方法,但是你不能在这里这样做,因为其他方法是在接口中定义的,因此必须是public
    • 其实现在我想多了,我意识到这并不能解决任何问题,因为子类不能覆盖你最终的before()after()
    【解决方案4】:

    一种选择是在ActualStuffTask 类中显式调用super.before()

    @Override
    public void before() {
        super.before();
        // init some things
        fancyNumber = 42;
    }
    

    另一种选择是更改父类的设计并使用 final 关键字“保护”before 方法:

    abstract class PoliteTask implements Task {
    
        @Override
        public final void before() {
            System.out.println("Hey");
            internalBefore();
        }
    
        protected abstract void internalBefore(); // child class should override this method
    
        ...
    
    }
    

    【讨论】:

      猜你喜欢
      • 2014-07-29
      • 2014-01-05
      • 2011-12-13
      • 1970-01-01
      • 2011-09-07
      • 2014-05-22
      • 1970-01-01
      • 1970-01-01
      • 2014-08-24
      相关资源
      最近更新 更多