在基类中编写需要在子类中覆盖的方法实现?

Posted

技术标签:

【中文标题】在基类中编写需要在子类中覆盖的方法实现?【英文标题】:Write a method implementation in base class which requires override in subclasses? 【发布时间】:2011-11-13 20:26:11 【问题描述】:

在 Java 中...

我正在创建一个包含方法 doAction() 的类 Foo。我的要求:

    doAction() 在 Foo 中必须有一个默认实现(即函数体)。 Foo 的所有子类都必须覆盖 doAction(),这意味着如果子类不提供新的实现,它们将获得编译器错误。 我需要能够实例化 Foo。

abstract 可以,但它不允许我为 doAction() 指定函数体。

【问题讨论】:

吃蛋糕还是吃蛋糕;不能两者兼得…… 为什么必须 doAction()有一个默认实现,如果它只是被子类忽略? 我需要自己运行/测试基类。 @frankadelic 我不建议在基类中添加专门用于测试的代码。这是不好的做法,会让未来的开发人员感到困惑,而且很难维护。你需要的是一个像 Rhino Mocks 这样的 Mocking 框架,为了测试目的,你可以指定你想要返回的内容。 【参考方案1】:

编辑

不可能同时满足所有要求,故事结束。您必须至少放弃一个条件,并且可能考虑一种完全不同的方法来解决您要解决的问题。


使用两种不同的方法。要么:

abstract class Foo 
    
    // Override this method
    abstract void doActionInSubclass();
    
    // You can't override a final method
    // And you don't want subclases to override this one
    final void doAction () 
        // do whatever default-y things you want here
        doActionInSubclass();
    

或者只是将“必需”方法与您想要强制子类覆盖的方法完全分开:

abstract class Foo 
    abstract void mustOverrideThisInConcreteSubclasses();
    
    final void doAction() 
        // default-y things here
    

【讨论】:

这个解决方案的问题是我无法实例化 Foo。我将更新我的问题以澄清此要求。 不可能同时满足所有要求,故事结束。 如果您希望实例化抽象类只是为了测试默认方法,即具体方法,您可以(在您的测试源代码中)创建一个TestFoo extends Foo,然后测试 那个.【参考方案2】:

如果在 Java 中为方法提供默认实现,则不能强制子类再次覆盖它。如果可以,请使用 template method 模式并使用不同的方法名称:

public class Foo

    public abstract void templateMethod();
    public final void doAction()

    //default implementation

     templateMethod(); // call template method
    

【讨论】:

doAction() 应该是最终的,因为有人可以在子类中重新定义它。【参考方案3】:

Foo 中实现您的方法并使其始终抛出异常,例如UnsupportedOperationException。这将要求子类覆盖该方法。

【讨论】:

并且用户可以捕获异常使程序继续运行。 他希望该方法具有默认实现,而不是抛出异常。【参考方案4】:

如果所有子类都应该覆盖它,为什么要提供默认实现?!这将毫无意义,因为没有人会使用它。但是,您可能有一些您希望由任何子类执行的 doAction 代码和一些您希望被覆盖的代码。 在这种情况下,您可以设计如下:

doAction()
     //put your default code here...
     specificAction();


abstract specificAction();

通过这种方式,doAction 有自己的实现,它由特定的Action 调用结束,而应该由子类来实现

【讨论】:

如您所见,您的问题有很多类似的答案:)【参考方案5】:

Foo 类的构造函数中,您可以使用reflection:

class Foo 
    public Foo() 
        Class c = getClass();
        if (!c.equals(Foo.class))
        
            // try/catch omitted for brevity
            Method m = c.getMethod("methodName", new Class[0]);
            if (m.getDeclaringClass().equals(Foo.class))
            
                throw new Exception("blah blah blah");
            
        
    

这不会产生编译时错误,但由于在构造函数中对 super() 的调用不能用 try 包装,因此任何创建子类的人都无法绕过它。

【讨论】:

以上是关于在基类中编写需要在子类中覆盖的方法实现?的主要内容,如果未能解决你的问题,请参考以下文章

C++的精髓——虚函数

在基类方法中引用继承类?

C#的构造函数在基类和父类中执行顺序

虚方法抽象方法接口方法

C#中基类属性值在子类中设置,如何在基类的方法中获取子类设置的值?

virtual的使用方法