3

我有一个只有一种方法的界面。我有一个接收对象的静态函数(这些对象实现接口),我想覆盖/替换在接口中定义的方法的实现,在对象中。在 Java 中执行此操作的最佳方法是什么

public class MyClass {

    public interface MyInterface {
         Object myMethod (Object blah);
    }

    public static MyInterface decorator(MyInterface obj) { 
          //I want to return a version of obj
          //with a different implementation of myMethod
          //everything else in obj should be same, except myMethod 
    } 
}
4

5 回答 5

2

您可以创建一个具有相同接口的类,该接口委托所有方法调用您的对象,然后创建一个匿名类扩展它并覆盖您想要的任何内容

接口:

Interface MyInterface {
    void m1();
    void m2();
}

委托类:

class MyDelegate implements MyInterface {
    private MyInterface delegate;

    MyDelegate(MyInterface delegate) {
        this.delegate = delegate;
    }

    void m1() {
        delegate.m1();
    }

    void m2() {
       delegate.m2();
    }
}

在静态方法中,您创建一个匿名类的实例,扩展MyDelegate并覆盖您想要的任何内容,其余的将由obj

static MyInterface wrap(MyInterface obj) {
    return new MyDelegate(obj) {
        void m1() {
            // overrided m1
        }

        // my will eventually get to obj
    };
}
于 2012-12-22T11:23:54.397 回答
1

如果不知道您正在装饰的确切对象类型,您通常无法做到这一点。如果您知道,那么您可以创建该特定类的类,并更改实现。Java 的类型系统不够灵活,无法像您需要的那样混合和匹配接口实现。

可以求助于动态类定义技术,这将为您传递给您的decorate方法的每个对象创建一个动态代理,但这种方法的复杂性要高一个数量级。

于 2012-12-22T11:30:27.413 回答
1

Java 不直接支持这种动态行为。但是当对象合作时,您可以实现类似的效果。即它可以提供一种方法来改变以下的实现myMethod

void changeMethod(MyInterface other) {
    realImpl = other;
}
Object myMethod (Object obj) {
    return realImpl.myMethod(obj);
}
于 2012-12-22T11:34:28.643 回答
0

使用CGLib,您可以在运行时实现接口。

例子 -

public class KeySample {
      private interface MyFactory {
         public Object newInstance(int a, char[] b, String d);
      }
      public static void main(String[] args) {
          MyFactory f = (MyFactory)KeyFactory.create(MyFactory.class);
          Object key1 = f.newInstance(20, new char[]{ 'a', 'b' }, "hello");
          Object key2 = f.newInstance(20, new char[]{ 'a', 'b' }, "hello");
          Object key3 = f.newInstance(20, new char[]{ 'a', '_' }, "hello");
          System.out.println(key1.equals(key2));
          System.out.println(key2.equals(key3));
      }
  }
于 2012-12-22T11:31:01.733 回答
0

如果我们可以将其限制为接口(任意数量),您正在寻找动态代理。

所以你的问题:如果你首先调用该接口中的方法而不调用装饰器,然后使用装饰器你想要不同的行为,那么给定这个接口实现:

MyInterface o = new MyInterface() {
    public Object myMethod(Object blah) {
        return "bar";
    }
};

这应该做不同的事情,例如改变返回值(和/或其他东西):

System.out.println(o.myMethod(null));
o = decorator(o);
System.out.println(o.myMethod(null));

输出:

bar
foo

这可以通过java中的动态代理来完成:

public static MyInterface decorator(final MyInterface obj) { 
  InvocationHandler handler = new InvocationHandler() {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      if (method.getName().equals("myMethod")) {
        //do something special
        return "foo";
      }
      return method.invoke(obj, args);
    }
  };


  MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
                            MyInterface.class.getClassLoader(),
                            obj.getClass().getInterfaces(), //all or limit it to just one or a few
                            handler);
  return proxy;
}

更新:如果您想涉及更多接口(上面更新了代理实例代码以实现接口而不是硬编码):

现在使这项工作:

 public static void main(String[] args) {
        A o = new A();
        System.out.println(o.myMethod(null));
        System.out.println(o.myOtherMethod(1));
        Object o2 = decorator(o);
        System.out.println(((MyInterface) o2).myMethod(null));
        System.out.println(((MyInterface2) o2).myOtherMethod(1));
 }

带输出:

bar
2
foo <-- changed
2 <- the same behavior

鉴于:

class A implements MyInterface, MyInterface2 {
  @Override
  public Object myMethod(Object blah) {
    return "bar";
  }

  @Override
  public int myOtherMethod(int a) {
    return a+1;
  }
}
于 2012-12-22T11:33:07.037 回答