【问题标题】:Overriding a method in an instantiated Java object覆盖实例化 Java 对象中的方法
【发布时间】:2011-04-21 23:58:24
【问题描述】:

我想重写一个我无法控制的工厂交给我的对象中的方法。

我的具体问题是我想覆盖 Socket objectgetInputStreamgetOutputStream 来执行 wire日志记录

一般问题如下:

public class Foo {
    public Bar doBar() {
        // Some activity
    }
}

我想使用实例化的Foo 并用我自己的替换doBar,其工作方式如下:

Bar doBar() {
    // My own activity
    return original.doBar();
}

对于 Socket,我将返回一个 InputStreamOutputStream,它们通过日志记录来截取数据。

【问题讨论】:

    标签: java oop methods overriding


    【解决方案1】:

    两个选项:

    1. easy :如果 Foo 是您实现的接口,您可以使用 Dynamic proxy 添加新功能。
    2. 更多工作:您所拥有的是 AOP 的“环绕”建议 - 您可以使用任何现有的 AOP 工具来实现这一点。如果您已经在使用 Spring Framework,它可以为您完成。

    【讨论】:

      【解决方案2】:

      如果 Socket 是一个接口,那么您可以创建一个动态代理。下面是一个例子。我把它放在这里以防其他人需要这样做并且目标实例是接口的实例。

      这对 Socket 不起作用的主要原因是因为java.lang.reflect.Proxy.newProxyInstance 需要一个接口数组作为其第二个参数,因此类在这里不起作用。因此,对于这个示例,我必须创建一个名为 ParentInterface 的接口,它只有三个打印方法。

      public class Parent implements ParentInterface {
      
          @Override
          public void print1() {
              System.out.println("parent 1");
          }
      
          @Override
          public void print2() {
              System.out.println("parent 2");
          }
      
          @Override
          public void print3() {
              System.out.println("parent 3");
          }
      
          public static void main(String[] args) {
              Parent originalInstance = new Parent();
              ParentInterface proxied = (ParentInterface) java.lang.reflect.Proxy.newProxyInstance(
                      Parent.class.getClassLoader(),
                      new Class[]{ParentInterface.class},
                      new ParentProxy(originalInstance));
      
              proxied.print1();
              proxied.print2();
              proxied.print3();
      
          }
      
          static class ParentProxy implements InvocationHandler {
      
              final Object realObject;
      
              public ParentProxy(Object real) {
                  realObject = real;
              }
      
              @Override
              public Object invoke(Object target, Method m, Object[] args) throws Throwable {
                  try {
                      if (m.getName().equals("print2")) {
                          print2();
                          return null;
                      } else {
                          return m.invoke(realObject, args);
                      }
                  } catch (java.lang.reflect.InvocationTargetException e) {
                      throw e.getTargetException();
                  }
              }
      
              public void print2() {
                  System.out.println("wrapper 2");
              }
      
          }
      
      }
      

      【讨论】:

        【解决方案3】:

        另一个与代理相关的解决方案:您可以使用 Aspects 覆盖给定对象的方法,而无需自己对其进行子类化。这对于日志记录特别合适且常见。本示例使用 spring-aop。

        class Example {
        
            final Foo foo;
        
            Example(Foo original) {
                AspectJProxyFactory factory = new AspectJProxyFactory();
                factory.setTarget(original);
                factory.addAspect(FooAspect.class);
                foo = (Foo) factory.getProxy();
            }
        
            @Aspect
            static class FooAspect {
        
                @Before("execution(Foo.doBar())")
                Object beforeDoBar() {
                    // My own activity
                }
            }
        

        【讨论】:

          【解决方案4】:

          我认为有一种方法可以达到你想要的效果。我看到它最初用在带有按钮的摇摆中,以允许程序员在单击按钮时使按钮执行某些操作。

          假设你有你的 Foo 类:

          public class Foo {
            public Bar doBar() {
              // Some activity
            }
          }
          

          然后你有一个跑步者类或类似的东西。您可以在实例化时重写 doBar() 方法,它只会影响该特定对象。

          该类可能如下所示:

          public class FooInstance{
            Foo F1 = new Foo(){
              public Bar doBar(){
                //new activity
              }
            }
          
            Foo F2 = new Foo();
          
            F1.doBar(); //does the new activity
            F2.doBar(); //does the original activity found in the class
          }
          

          我不完全确定这对你有用,但也许它会让你朝着正确的方向前进。如果没有其他方法可以覆盖类之外的方法,也许这会对您有所帮助。

          【讨论】:

          • 两者都将执行原始活动。使用匿名类覆盖仅适用于对象本身内部的方法。对于调用者(来自对象外部),您不能使用该方法的覆盖版本。
          • 我实际上只是测试了它,它可以工作。不知道你在说什么,Hazem。
          • 创建对象时,您正在覆盖该方法。但是问题表明他是从工厂获得的对象,所以该对象已经被创建。一旦对象已经创建,您就不能覆盖对象方法。
          【解决方案5】:

          使用装饰器是正确的方法:

          这里有一些与您对套接字的要求非常相似的代码:

          http://www.javaspecialists.eu/archive/Issue058.html

          【讨论】:

            【解决方案6】:

            你不能真正在 java 中动态地改变一个对象。

            您可以通过将Foo 包装到另一个类似的对象中来做您想做的事情,该对象会将每次调用委托给Foo 并同时记录您想要的所有内容。 (见Proxy

            但是如果你想做日志,也许方面是更好的选择。 (见AspectJ

            【讨论】:

              【解决方案7】:

              我不确定这是否可能。您是否考虑过创建自己的类,将工厂返回的对象作为成员,然后为该类编写 doBar() 方法。

              【讨论】:

                【解决方案8】:

                由于 Java 使用基于类的 OO,这是不可能的。你可以做的是使用decorator pattern,即为返回包装流的对象编写一个包装器。

                【讨论】:

                • 装饰 Socket 真的很痛苦,但这似乎是我唯一的选择。谢谢。
                • 嗨,我尝试使用装饰器,但实际上我尝试装饰的对象有一些它在内部使用的受保护方法,我无法覆盖这些方法,因为我必须从我的实例中调用它们不可能的对象,因为它们受到保护
                • @YAT 你可以考虑反思。它通常被认为是糟糕的形式并且非常hacky,但它可以工作。喜欢:Method[] methods = object.getClass().getDeclaredMethods(); methods[0].setAccessible(true); methods[0].invoke(object, arg1, arg2);
                • @Erhannis 反射是一个糟糕的主意,尤其是对于涉及网络/数据的事情。
                【解决方案9】:

                您无法替换现有对象中的方法 - 一方面,您无法更改现有对象的类型。

                您可以创建另一个类的新实例委派给现有实例,但这也有限制。

                在您的实际情况中,您是否无法简单地进行单独调用来包装套接字返回的流?能否提供更多细节。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2012-07-19
                  • 2017-10-08
                  • 1970-01-01
                  • 2023-02-09
                  • 2010-10-27
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多