有没有一种方法可以确定Java类中的方法是否被覆盖?

28
我希望能够确定一个基类方法是否被子类覆盖,因为在调用它之前需要进行昂贵的设置,并且我们系统中的大多数子类没有覆盖它。能否使用反射提供的方法句柄进行测试?或者还有其他方法可以测试类方法是否被覆盖?
例如:
class BaseClass {
    void aMethod() { // don nothing }

    protected boolean aMethodHasBeenOverridden() {
        return( // determine if aMethod has been overridden by a subclass);
    } 
}
3个回答

31

通过检查方法的声明类,您可以使用反射来实现:

class Base {
    public void foo() {}
    public void bar() {}
}
class Derived extends Base {
    @Override   
    public void bar() {}
}
...
Method mfoo = Derived.class.getMethod("foo");
boolean ovrFoo = mfoo.getDeclaringClass() != Base.class;
Method mbar = Derived.class.getMethod("bar");
boolean ovrBar = mbar.getDeclaringClass() != Base.class;
System.out.println("Have override for foo: "+ovrFoo);
System.out.println("Have override for bar: "+ovrBar);

打印

Have override for foo: false
Have override for bar: true

演示。


1
如果在基类和叶子类之间的中间类上重写了方法,会出现问题。 - chrylis -cautiouslyoptimistic-
1
@chrylis 感谢您指出这一点。将声明类与Base.class进行比较可以解决此问题。 - Sergey Kalinichenko
小注 - C++ 使用术语“基类”和“派生类”; Java 使用术语“超类”和“子类”。 - Nayuki
3
@Nayuki:在Java示例中使用class Baseclass Derived extends Base也很常见;而class Superclass Sub则较少见,可能是因为super是一个关键字,而Sub看起来有点奇怪。 - wchargin
此外,当讨论层次结构而不仅仅是两个级别时,“base”通常用于指示所涉及的最高级别的类。 - chrylis -cautiouslyoptimistic-

9

要实现这一点,可以调用 getClass().getDeclaredMethod("aMethod") 方法,只有当this的类声明了它时,该方法才会返回值。

以下是您方法的一个实现:

/**
 * @return true if the instance's class overrode aMethod
 */
protected boolean aMethodHasBeenOverridden() {
    try {
        return getClass() != A.class && getClass().getDeclaredMethod("aMethod") != null;
    } catch (NoSuchMethodException | SecurityException e) {
        return false;
    }
} 

2

我会采取的方法是,只有在子类需要它时才使该方法存在,通过在抽象中间类中覆盖调用它的方法来实现。以下是代码示例:

public abstract class MovingThing {
    public void move() {
        // walk a few feet
    }
}

现在你的一些移动物品可以进行瞬间传送,但这需要充电通量电容器和其他昂贵的设备,所以请将其分开处理:

public abstract class TeleportingThing extends MovingThing {
    @Override
    public void move() {
        fluxCapacitor.charge();
        stardate.calculate();
        doTeleport();
    }

    protected abstract void doTeleport();
}

需要进行昂贵设置的类从包含它的第二个类中派生,而不需要进行设置的类可以从第一个类派生。这种模式是一种装饰器,例如在 Servlet API 中使用,在那里大多数 servlet 重写类似于 doGet() 的东西,并将解析留给 service()


问题在于,这会使实现细节(即如何避免昂贵的设置,如果不是严格必要的)影响公共API(即继承链)。更糟糕的是,在Java中,它仅适用于单个方法,因为您只能从单个基类继承。像OP问题中所述的那样,仅具有protected boolean aMethodHasBeenOverridden()方法可能乍一看起来不太优雅,但实际上更可取,因为它没有任何这些缺点... - fgp
@fgp 把那个方法放到类里面会暴露公共API的问题完全相同,而我的技巧可以应用于任何一组可能被覆盖的方法。 - chrylis -cautiouslyoptimistic-
你的方法将选择基类与是否覆盖doTeleport()耦合在一起,因此如果你有两个这样的方法并且想要覆盖其中一个但不是另一个,你该怎么做? - fgp
@fgp 像Servlet一样吗?(虽然我个人更喜欢Groovy traits。) - chrylis -cautiouslyoptimistic-
我不知道 Servlet 如何处理事情,所以,嗯,也许... :-) 无论如何,让我们就你的方法是否优雅或过于复杂达成不同意见吧 - 有时这种区别更多是品味问题而已... - fgp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接