【问题标题】:How to quickly determine if a method is overridden in JavaJava如何快速判断一个方法是否被覆盖
【发布时间】:2011-01-19 21:39:37
【问题描述】:

如果我可以确定同一个类中的另一个方法没有被覆盖,我可以对我的一个方法应用一种可能的优化。这只是一个轻微的优化,所以反射是不可能的。我是否应该只创建一个受保护的方法来返回相关方法是否被覆盖,以便子类可以使其返回 true?

【问题讨论】:

  • 不优化怎么样?听起来优化还不够重要。
  • 匿名:该方法每秒被调用数百次。
  • 然后进行反射,并将结果缓存在私有字段中。
  • 匿名:有可能,只是有点不干净。

标签: java oop optimization methods overriding


【解决方案1】:

我不会这样做。它违反了封装,并在实现者不知道的情况下改变了你的类应该做什么的契约。

如果你必须这样做,最好的方法是调用

class.getMethod("myMethod").getDeclaringClass();

如果返回的类是你自己的,那么它不会被覆盖;如果是其他东西,则该子类已覆盖它。是的,这是反射,但它仍然很便宜。

不过,我确实喜欢您的保护方法方法。看起来像这样:

public class ExpensiveStrategy {
  public void expensiveMethod() {
    // ...
    if (employOptimization()) {
      // take a shortcut
    }
  }

  protected boolean employOptimization() {
    return false;
  }
}

public class TargetedStrategy extends ExpensiveStrategy {
  @Override
  protected boolean employOptimization() {
    return true; // Now we can shortcut ExpensiveStrategy.
  }
}

【讨论】:

  • 嗯,我的优化是逐个案例的小产量,它只会加快速度,因为它每秒调用数百次。通过反射,该产量将降低到优化毫无意义的程度。一个很好的答案,所以无论如何我都赞成。
  • 如果您真的需要它,您可以确定方法是否通过静态初始化程序重载并将结果存储在布尔值中。或者,您可以通过 aspectj 添加它
  • 使用 Class.getMethod 代替。如果您询问子类未覆盖的继承方法,Class.getDeclaredMethod 将引发 NoSuchMethodException。当然,您可以将异常用作方法未被覆盖的(不良)指示符。
  • @vickirk:静态缓存不起作用。超类不知道(没有反射)哪个子类正在调用它,因此它无法缓存结果。如果稍后有不同的子类调用它,则缓存的结果将是错误的。
  • Noel:不仅是反射,而且 try...catch 块也很慢。
【解决方案2】:

嗯,我的优化是逐个案例的小产量,它只会加快速度,因为它每秒被调用数百次。

您可能想看看 Java 优化器能做什么。您的手动编码优化可能不是必需的。

如果您认为手动编码优化是必要的,那么您描述的受保护方法方法不是一个好主意,因为它会暴露您的实现细节。

【讨论】:

  • +1 对我来说似乎是个好建议,不知道为什么它被否决了。
【解决方案3】:

您希望函数在程序的生命周期内被调用多少次?对特定单一方法的反思应该不会太糟糕。如果在程序的生命周期内不值得花那么多时间,我的建议是保持简单,不要包括小的优化。

雅各布

【讨论】:

  • 这是一种很常用的方法。
  • 让我澄清一下我的答案。我建议如果你有这种通用模式,并且实例都是相同的类型,你可以缓存一次执行的反射的值。您第一次确定配置,并始终使用该值。这样一来,反射开销将是一次,而调用的数量会大得多。如果在每个实例上重复调用它,在父类中声明并在第一次调用时延迟初始化的实例变量(使用反射)可能会给你一个提升。
【解决方案4】:

注释覆盖特定方法的子类。 @OverridesMethodX。

对类加载执行必要的反射工作(即在static 块中),以便通过最终布尔标志发布信息。然后,在需要的位置和时间查询标志。

【讨论】:

  • 静态块破坏继承。
  • 我知道它有味道。但是你想要的优化已经在颠覆多态了。
【解决方案5】:

也许有一种更简洁的方法可以通过Strategy Pattern 做到这一点,虽然我不知道你的应用程序和数据的其余部分是如何建模的,但它似乎适合。

无论如何,当我遇到类似问题时,它确实对我有用。您可以有一个启发式方法,根据要处理的数据决定使用哪种策略。

再一次,我没有足够的信息来说明你的具体用法,看看这是否是矫枉过正。但是,我会避免更改此类特定优化的类签名。通常,当我有逆流而上的冲动时,我会认为我在设计这个东西时没有预见到一个角落案例,我应该将它重构为一个更简洁更全面的解决方案。

但请注意,仅出于优化目的进行此类重构几乎不可避免地会导致灾难。如果是这种情况,我会采用上面建议的反思方法。它不会改变继承契约,并且在正确完成后,只需要在应用程序的运行时生命周期中需要它的每个子类完成一次。

【讨论】:

    【解决方案6】:

    我知道这是一个有点老的问题,但为了其他谷歌员工:

    我想出了一个使用接口的不同解决方案。

    class FastSub extends Super {}
    class SlowSub extends Super implements Super.LetMeHandleThis {
        void doSomethingSlow() {
            //not optimized
        }
    }
    class Super {
        static interface LetMeHandleThis {
            void doSomethingSlow();
        }
        void doSomething() {
            if (this instanceof LetMeHandleThis)
                ((LetMeHandleThis) this).doSomethingSlow();
            else
                doSomethingFast();
        }
        private final void doSomethingFast() {
            //optimized
        }
    }
    

    或者反过来:

    class FastSub extends Super implements Super.OptimizeMe {}
    class SlowSub extends Super {
        void doSomethingSlow() {
            //not optimized
        }
    }
    class Super {
        static interface OptimizeMe {}
        void doSomething() {
            if (this instanceof OptimizeMe)
                doSomethingFast();
            else
                doSomethingSlow();
        }
        private final void doSomethingFast() {
            //optimized
        }
        void doSomethingSlow(){}
    }
    

    【讨论】:

      【解决方案7】:
      private static boolean isMethodImplemented(Object obj, String name)
      {
          try
          {
              Class<? extends Object> clazz = obj.getClass();
      
              return clazz.getMethod(name).getDeclaringClass().equals(clazz);
          }
          catch (SecurityException e)
          {
              log.error("{}", e);
          }
          catch (NoSuchMethodException e)
          {
              log.error("{}", e);
          }
      
          return false;
      }
      

      【讨论】:

      • 您在这里和here 发布了完全相同的答案。如果你能给出完全相同的答案,那么你应该将问题标记为重复,不重复答案。
      【解决方案8】:

      反射可用于确定方法是否被覆盖。代码有点棘手。例如,您需要知道您有一个运行时类,它是覆盖该方法的类的子类。

      您将一遍又一遍地看到相同的运行时类。因此,您可以将检查结果保存在WeakHashMap 中键入Class。

      请参阅我在java.awt.Component 中处理coalesceEvents 的代码作为示例。

      【讨论】:

      • @PiPeep:实际实例无关紧要。正如我所说,映射键基于运行时类。
      【解决方案9】:

      这可能是另一种解决方法,类似于覆盖另一个受保护的方法返回真/假

      我建议创建一个空接口,标记接口,然后让子类实现这个接口,并在超类内部检查这个实例是instanceof这个接口,然后再调用被覆盖的昂贵方法。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-02-28
        相关资源
        最近更新 更多