Java 强制子类重写这两个方法或不重写

Java 强制子类重写这两个方法或不重写,java,Java,假设我有一个基类和一对方法(称为foo和bar)。在大多数情况下,它们不需要被覆盖,但在某些情况下,它们确实需要被覆盖。我想确定如果其中一个被覆盖,那么另一个也必须被覆盖,否则这是一个错误 我可以使用什么技巧来确保这两个方法中的任何一个都不被重写,或者两个都被重写?您不能将它们都放在一个接口中,让客户端决定是否实现它们吗。 通过这种方式,如果有人实现了这个接口,或者没有人实现这个接口,这两个接口都会被覆盖。这已经被证明是一个有趣的思想练习。我能想出的最佳解决方案如下: 为方法声明接口: publ

假设我有一个基类和一对方法(称为
foo
bar
)。在大多数情况下,它们不需要被覆盖,但在某些情况下,它们确实需要被覆盖。我想确定如果其中一个被覆盖,那么另一个也必须被覆盖,否则这是一个错误


我可以使用什么技巧来确保这两个方法中的任何一个都不被重写,或者两个都被重写?

您不能将它们都放在一个接口中,让客户端决定是否实现它们吗。
通过这种方式,如果有人实现了这个接口,或者没有人实现这个接口,这两个接口都会被覆盖。这已经被证明是一个有趣的思想练习。我能想出的最佳解决方案如下:

为方法声明接口:

public interface YourInterface {
  public void methodOne();  
  public void methodTwo();
}
创建一个基类,通过委托给内部
YourInterface
实例来实现这些方法。在受保护的构造函数中获取一个参数,该参数覆盖默认行为:

public abstract class Base implements YourInterface {

  private YourInterface override;

  protected Base(YourInterface override) {
    this.override = (override == null) ? new BaseImplementation() : override;
  }

  @Override
  public final void methodOne() {
    override.methodOne();
  }

  @Override
  public final void methodTwo() {
    override.methodTwo();
  }

  // This is the default implementation
  private static class BaseImplementation implements YourInterface {

    @Override
    public void methodOne() {
      System.out.println("Original one.");      
    }

    @Override
    public void methodTwo() {
      System.out.println("Original two.");
    }    
  }
}
这两个方法在基类中是
final
,因此子类不能只重写其中一个


不一定是一个优雅或可取的解决方案。

没有简单的方法可以做到这一点。有一种方法存在,但我不知道它是否真的能满足您的需要。我认为你真正应该考虑的是为什么你想要这个,如果一个不同的构造(抽象类?AOP?其他的东西)会更好地满足你的设计需求。

您可以编写一个代码段来快速确定某个方法是否已被重写:

String declaredIn=obj.getMethod(“myMethod”).getDeclaringClass()

如果重写,则返回当前类名;如果未重写,则返回基类名


如果您需要任何子类在
foo
bar
中调用
super.foo
super.bar
(您可以使用非无参数构造函数之类的东西来实现),您可以在基方法中实现这一点:检查两个方法的声明类是否相同,然后抛出一个异常。

如果您有一个方法,并且希望确保它被重写,我认为类似的方法可以工作:

public class C {
    public void foo() {
        if (this.getClass() != C.class)
            throw new RuntimeException ("foo was not overridden for class " + this.getClass().getName());
...
但确保两个方法都被覆盖或不被覆盖似乎要复杂得多。这样如何:与其让子类重写
foo
bar
,不如创建
foo
bar
final
方法,并提供受保护的“实现”方法,在这些方法中类必须重写两者或两者都不重写。然后:

public class C {

    private boolean fooCalled = false;
    private boolean barCalled = false;
    private boolean fooDefault = false;
    private boolean barDefault = false;

    public final void foo() {
        fooCalled = true;
        fooImpl();
        checkOverrides();
    }

    public final void bar() {
        barCalled = true;
        barImpl();
        checkOverrides();
    }

    private void checkOverrides() {
        if (this.getClass() != C.class && fooCalled && barCalled && 
            (fooDefault != barDefault)) 
            throw new RuntimeException ("foo or bar was overridden without the other for class " + this.getClass().getName();
    }

    protected void fooImpl() {
        fooDefault = true;
        // the rest of the default implementation
    }

    protected void barImpl() {
        barDefault = true;
        // the rest of the default implementation
    }

}
foo
bar
都至少被调用一次之前,我们无法进行检查(如果其中任何一个都没有被调用,那么它是否被重写就无关紧要了)。调用这两个方法后,我们将知道是否覆盖了每个方法(“Impl”版本),因为调用默认实现时将设置一个标志。然后,我们可以根据需要对信息进行任何检查

如果重写的
fooImpl
调用
super.fooImpl()
(类似于
bar
),则此操作将失败,但我的猜测是,如果您预期会发生这种情况,那么如果其中一个被重写,则两者都被重写的要求可能没有意义。如果仍然需要,可以提供一个单独的
protected
default实现方法,该方法不设置
fooDefault
标志,客户端只需小心使用它而不是
super
方法


写了所有这些,我很难想象像这样的解决方案真的是必要的。也许你有一个值得这么做的案例。

你可以在你的类中添加一个构造函数,并使用反射来检查覆盖了哪个类的方法,如果只有一个被覆盖,则抛出一个错误

可以是这样的(test是我使用的基类的名称):


在中存在类似的问题,使用
equals(Object)
hashCode()
方法

如果您想绝对确保为这两种方法都提供实现,那么可以通过将功能包装在一个接口中来提供一个优雅的解决方案,该接口将在构建过程中传入—它可以保证两种方法的默认实现,也可以保证两种方法的不同实现

但是,如果您的案例更像
对象中的案例,它们不需要被覆盖,只需要满足一个非常具体的契约,那么关于契约是什么的清晰文档将更好地工作,允许开发人员以他们确定的最佳方式满足契约

  • 使用目标方法具有标记接口@overridererequired
  • 用@overridererequired注释所需的方法
  • 使用反射编写validateOverrideRequired(类…类),如果在子类中未重写注释为@OverrideRequired的方法,则会引发RuntimeException?按照规则,每个构建调用一次
  • 以上只是一个概念,;不确定实施情况


    但这是一个有趣的问题&来自小组的同样有趣的答案(rest)。

    你不能强迫用户这样做,你只能记录它,如果他们不这样做,那就是他们的错。听起来,有问题的类应该继承这些方法的默认行为。因此,您的解决方案不会真正起作用,因为基类已经实现了这个接口。是的@Duncan,但这将确保两个方法中的任何一个都实现了,这不是OP想要的。OP希望确保这两个类都被覆盖或不被覆盖。如果子类有一个深树:
    a->B->C
    ,其中一个在
    B
    中实现,另一个在
    C
    中实现,那么对于类
    C
    ,该测试将不正确地通过。这是个好主意的开始。克里斯,你可以改变条件
    public test() {
        Class clazz = this.getClass();
        try {
            Method m = clazz.getMethod("foo", new Class[]{});
            Class cFoo = m.getDeclaringClass();
            m = clazz.getMethod("bar", new Class[]{});
            Class cBar = m.getDeclaringClass();
            if (cFoo.equals(test.class)&&!cBar.equals(test.class)) throw new Error();
            if (!cFoo.equals(test.class)&&cBar.equals(test.class)) throw new Error();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }