假设我有一个带有一对方法的基类(称为foo
和bar
)。在大多数情况下,他们不需要被覆盖,但在某些情况下,他们会这样做。我想确保如果其中一个被覆盖,那么另一个也必须被覆盖,或者它是一个错误。
我可以使用哪些技巧来确保两种方法都不被覆盖,或者两种方法都被覆盖?
答案 0 :(得分:5)
这被证明是一项有趣的思想练习。我能想出的最佳解决方案如下:
为您的方法声明一个接口:
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
,因此子类不能只覆盖其中一个。
不一定是优雅或可取的解决方案。
答案 1 :(得分:1)
没有简单的方法可以做到这一点。存在一种方法,但我不知道它是否真的能满足您的需求。我认为你应该真正考虑的是为什么你想要这个,如果一个不同的构造(抽象类?AOP?还有什么?)将更好地满足你的设计需求。
您可以编写一个代码段来快速确定是否已覆盖某个方法:
String declaredIn = obj.getMethod("myMethod").getDeclaringClass();
如果被覆盖,这将返回当前的类名,如果不是,则返回基类名。
如果您需要任何子类来调用super.foo
和super.bar
中的foo
和bar
(您可以使用非参数构造函数强制执行),则可以强制执行这在基本方法中:检查两个方法的声明类是否相同,并抛出异常。
答案 2 :(得分:1)
使用equals(Object)
和hashCode()
方法在Object
中存在类似问题。
如果你想绝对确保为两者都提供实现,或者两者都不提供,Duncan's answer通过将功能包装在构造期间传入的接口中为此提供了一个优雅的解决方案 - 它保证默认两种方法的实现,或两者的不同实现。
但是,如果您的案例更像Object
中的情况,他们不会需要被覆盖,但只是满足一个非常具体的合同,那么请清楚说明这是什么合同将更好地工作,允许开发人员以他们认为最好的方式满足合同。
答案 3 :(得分:0)
如果您有一种方法并且您想确保它被覆盖,我认为这样的方法会起作用:
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
默认实现方法,该方法不设置fooDefault
标志,客户端必须小心使用它而不是super
方法
写完这一切之后,我很难想象这样的解决方案真的是必要的。也许你有一个值得的案例。
答案 4 :(得分:0)
你可以在你的类中添加一个构造函数,并使用反射来检查方法被覆盖的类,如果只有一个被覆盖,则抛出一个错误。
可以是这样的(test是我使用的基类的名称):
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();
}
}
答案 5 :(得分:0)
以上只是一个概念;不确定实施。
但是一个有趣的问题&同样有趣的答案(休息)来自小组。
答案 6 :(得分:-1)
你不能把它们都放到一个接口中,让客户决定是否实现它们。 这样,如果某些实现此接口,或者如果没有人实现
,则覆盖它们中的任何一个