【问题标题】:Is there a way to determine if a method has been overridden in a Java class有没有办法确定Java类中的方法是否已被覆盖
【发布时间】:2016-07-24 17:27:08
【问题描述】:

我希望能够确定基类方法是否已被子类覆盖,特别是因为在调用它之前需要进行昂贵的设置,并且我们系统中的大多数子类都不会覆盖它。是否可以使用反射提供的方法句柄进行测试?或者是否有其他方法可以测试类方法是否被覆盖?

例如

class BaseClass {
    void aMethod() { // don nothing }

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

【问题讨论】:

    标签: java oop overriding


    【解决方案1】:

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

    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
    

    Demo.

    【讨论】:

    • 如果该方法在基类和叶类之间的中间类上被覆盖,则会出现问题。
    • @chrylis 感谢您指出这一点。将声明类与Base.class 进行比较可以解决此问题。
    • 次要注意 - C++ 使用术语基类派生类; Java 使用术语 superclasssubclass
    • @Nayuki:不过,在 Java 示例中使用 class Baseclass Derived extends Base 也很常见; class Superclass Sub 不太常见,可能是因为 super 是关键字,而 Sub 看起来有点奇怪。
    • @Nayuki 此外,当谈到层次结构而不是简单的两个级别时,“base”通常用于表示所涉及的最高级类。
    【解决方案2】:

    这可以通过调用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;
        }
    } 
    

    【讨论】:

      【解决方案3】:

      我将采取的方法是仅在子类需要时通过覆盖在抽象中间类中调用它的方法来使此方法存在。这就是它的样子:

      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(即继承链)。更糟糕的是,它仅适用于 single 方法(至少在 Java 中),因为您只能从单个基类继承。像 OP 的问题那样简单地使用 protected boolean aMethodHasBeenOverridden() 方法乍一看可能看起来不那么优雅,但实际上更可取,因为它没有任何这些缺点......
      • @fgp 将该方法放入类中与公开公共 API 的问题完全相同,并且我的技术可以应用于任何可能被覆盖的方法集。
      • 你的方法将基类的选择与是否覆盖 doTeleport() 结合起来,所以如果你有两个这样的方法并且想要覆盖其中一个而不是另一个,你会怎么做呢?
      • @fgp 在 Servlet 中? (虽然此时我自己会喜欢 Groovy 特性。)
      • 我不知道Servlet 是如何处理事情的,所以,嗯,也许... :-) 无论如何,让我们同意不同意您的方法是优雅还是过于复杂 - 有时区别更多的是品味问题而不是其他任何东西......
      猜你喜欢
      • 2010-09-06
      • 2016-10-01
      • 2010-10-15
      • 1970-01-01
      • 1970-01-01
      • 2020-08-29
      • 2020-08-02
      • 1970-01-01
      相关资源
      最近更新 更多