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();
}
}