自动委托java类的所有方法

假设我有一个包含许多公共方法的类:

public class MyClass { public void method1() {} public void method2() {} (...) public void methodN() {} } 

现在我想创建一个包装类,它将所有方法委托给包装实例( 委托 ):

 public class WrapperClass extends MyClass { private final MyClass delegate; public WrapperClass(MyClass delegate) { this.delagate = delegate; } public void method1() { delegate.method1(); } public void method2() { delegate.method2(); } (...) public void methodN() { delegate.methodN(); } } 

现在,如果MyClass有很多方法,我需要覆盖它们中的每一个,这或多或少是相同的代码,只是“委托”。 我想知道是否有可能做一些魔术来自动调用Java中的方法(所以Wrapper类需要说“嘿,如果你在我身上调用一个方法,只需去委托对象并在其上调用此方法)”。

顺便说一句:我不能使用inheritance,因为委托不在我的控制之下。我只是从其他地方得到它的实例(另一种情况是如果MyClass是最终的)。

注意:我不想生成IDE。 我知道我可以在IntelliJ / Eclipse的帮助下完成它,但我很好奇是否可以在代码中完成。

有任何建议如何实现这样的事情? (注意:我可能会在某些脚本语言中执行此操作,例如php,我可以使用php魔术函数来拦截调用)。

也许java的动态Proxy可以帮到你。 它只适用于您因此使用接口。 在这种情况下,我将调用接口MyInterface并设置默认实现:

 public class MyClass implements MyInterface { @Override public void method1() { System.out.println("foo1"); } @Override public void method2() { System.out.println("foo2"); } @Override public void methodN() { System.out.println("fooN"); } public static void main(String[] args) { MyClass wrapped = new MyClass(); wrapped.method1(); wrapped.method2(); MyInterface wrapper = WrapperClass.wrap(wrapped); wrapper.method1(); wrapper.method2(); } } 

包装类实现如下所示:

 public class WrapperClass extends MyClass implements MyInterface, InvocationHandler { private final MyClass delegate; public WrapperClass(MyClass delegate) { this.delegate = delegate; } public static MyInterface wrap(MyClass wrapped) { return (MyInterface) Proxy.newProxyInstance(MyClass.class.getClassLoader(), new Class[] { MyInterface.class }, new WrapperClass(wrapped)); } //you may skip this definition, it is only for demonstration public void method1() { System.out.println("bar"); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Method m = findMethod(this.getClass(), method); if (m != null) { return m.invoke(this, args); } m = findMethod(delegate.getClass(), method); if (m != null) { return m.invoke(delegate, args); } return null; } private Method findMethod(Class clazz, Method method) throws Throwable { try { return clazz.getDeclaredMethod(method.getName(), method.getParameterTypes()); } catch (NoSuchMethodException e) { return null; } } } 

注意这个类:

  • 扩展MyClass ,inheritance默认实现(任何其他都可以)
  • 实现Invocationhandler ,允许代理进行reflection
  • 可选地实现MyInterface (以满足装饰器模式)

此解决方案允许您覆盖特殊方法,但可以委派所有其他方法。 这甚至适用于Wrapper类的子类。

请注意,方法findMethod尚未捕获特殊情况。

这个问题已经有6个月了,@ CoronA的精彩回答已经得到了@walkeros的满意和接受,但我想我会在这里添加一些内容,因为我认为这可以推动一个额外的步骤。

正如@CoronA在回答的评论中所讨论的那样,动态代理解决方案不是必须在WrapperClass创建和维护一长串MyClass方法(即public void methodN() { delegate.methodN(); } ),到界面。 问题是你仍然需要在界面中为MyClass方法创建和维护一长串签名,这可能有点简单,但并不能完全解决问题。 如果您无法访问MyClass以了解所有方法,则尤其如此。

根据三种装饰代码的方法 ,

对于更长的类,程序员必须选择两个邪恶中的较小者:实现许多包装器方法并保持装饰对象的类型或维护简单的装饰器实现并牺牲保留装饰对象类型。

所以这可能是装饰模式的预期限制。

然而,@ Mark-Bramnik在插入Java类方法(没有接口)时 使用CGLIB提供了一个引人入胜的解决方案 。 我能够将它与@ CoronaA的解决方案结合起来,以创建一个可以覆盖单个方法但随后将所有其他内容传递给包装对象而不需要接口的包装器。

这是MyClass

 public class MyClass { public void method1() { System.out.println("This is method 1 - " + this); } public void method2() { System.out.println("This is method 2 - " + this); } public void method3() { System.out.println("This is method 3 - " + this); } public void methodN() { System.out.println("This is method N - " + this); } } 

这里是WrapperClass ,它只覆盖method2() 。 正如您将在下面看到的那样,未覆盖的方法实际上并未传递给委托,这可能是一个问题。

 public class WrapperClass extends MyClass { private MyClass delagate; public WrapperClass(MyClass delegate) { this.delagate = delegate; } @Override public void method2() { System.out.println("This is overridden method 2 - " + delagate); } } 

这是扩展MyClass MyInterceptor 。 它采用了使用CGLIB的代理解决方案,如@ Mark-Bramnik所述。 它还使用@CononA的方法来确定是否将方法发送到包装器(如果被覆盖)或包装对象(如果不是)。

 import java.lang.reflect.Method; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class MyInterceptor extends MyClass implements MethodInterceptor { private Object realObj; public MyInterceptor(Object obj) { this.realObj = obj; } @Override public void method2() { System.out.println("This is overridden method 2 - " + realObj); } @Override public Object intercept(Object arg0, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { Method m = findMethod(this.getClass(), method); if (m != null) { return m.invoke(this, objects); } Object res = method.invoke(realObj, objects); return res; } private Method findMethod(Class clazz, Method method) throws Throwable { try { return clazz.getDeclaredMethod(method.getName(), method.getParameterTypes()); } catch (NoSuchMethodException e) { return null; } } } 

这是Main以及运行它时得到的结果。

 import net.sf.cglib.proxy.Enhancer; public class Main { private static MyClass unwrapped; private static WrapperClass wrapped; private static MyClass proxified; public static void main(String[] args) { unwrapped = new MyClass(); System.out.println(">>> Methods from the unwrapped object:"); unwrapped.method1(); unwrapped.method2(); unwrapped.method3(); wrapped = new WrapperClass(unwrapped); System.out.println(">>> Methods from the wrapped object:"); wrapped.method1(); wrapped.method2(); wrapped.method3(); proxified = createProxy(unwrapped); System.out.println(">>> Methods from the proxy object:"); proxified.method1(); proxified.method2(); proxified.method3(); } @SuppressWarnings("unchecked") public static  T createProxy(T obj) { Enhancer e = new Enhancer(); e.setSuperclass(obj.getClass()); e.setCallback(new MyInterceptor(obj)); T proxifiedObj = (T) e.create(); return proxifiedObj; } } >>> Methods from the unwrapped object: This is method 1 - MyClass@e26db62 This is method 2 - MyClass@e26db62 This is method 3 - MyClass@e26db62 >>> Methods from the wrapped object: This is method 1 - WrapperClass@7b7035c6 This is overridden method 2 - MyClass@e26db62 This is method 3 - WrapperClass@7b7035c6 >>> Methods from the proxy object: This is method 1 - MyClass@e26db62 This is overridden method 2 - MyClass@e26db62 This is method 3 - MyClass@e26db62 

正如您所看到的,当您在method3()上运行方法时,您将获得未被覆盖的方法的包装器(即method1()method3() )。 但是,当您在proxified上运行方法时,所有方法都在包装对象上运行,而不必在WrapperClass它们全部委托给所有方法,或者将所有方法签名放在接口中。 感谢@CoronA和@Mark-Bramnik对这个问题的一个非常酷的解决方案。

切换到Groovy 🙂

 @CompileStatic public class WrapperClass extends MyClass { @Delegate private final MyClass delegate; public WrapperClass(MyClass delegate) { this.delagate = delegate; } //Done. That's it. } 

http://mrhaki.blogspot.com/2009/08/groovy-goodness-delegate-to-simplify.html

从Lombok框架检查@Delegation注释: https : //projectlombok.org/features/Delegate.html

您不必这样做 – 您的Wrapper类是原始类的子类,因此它inheritance了所有可公开访问的方法 – 如果您不实现它们,将调用原始方法。

你不应该extends Myclass与私有的MyClass对象一起extends Myclass – 这真的是多余的,我无法想到这样做的设计模式是正确的。 您的WrapperClass 一个MyClass ,因此您可以使用自己的字段和方法而不是调用delegate

编辑:在MyClassfinal的情况下,你将绕过willfull声明,不允许通过“伪造”inheritance进行子类化; 除了你之外,我想不出有谁愿意这样做,谁控制着WrapperClass ; 但是,既然你控制了WrapperClass ,那么不包装你不需要的东西实际上不仅仅是一个选项 – 这是正确的做法,因为你的对象不是 MyClass ,并且只应该像你在心理上考虑的案例。

编辑你刚刚通过将MyClass超类删除到WrapperClass来改变你的问题意味着完全不同的东西; 这有点糟糕,因为它使到目前为止给出的所有答案都无效。 你应该打开另一个问题。

WrapperClass定义一个方法,即delegate() ,它返回MyClass的实例

要么

您可以使用reflection来执行此操作,但调用者必须将方法名称作为参数传递给公开的方法。 并且有关方法参数/重载方法等的复杂性。

顺便说一句:我不能使用inheritance,因为委托不在我的控制范围内。我只是从其他地方得到它的实例(另一种情况是如果MyClass是最终的)

您发布的代码具有public class WrapperClass extends MyClass

实际上,你当前的WrapperClass实现实际上是MyClass之上的装饰器

Credits转到CoronA指出Proxy和InvocationHandler类。 我使用generics基于他的解决方案制定了一个更可重用的实用程序类:

 public class DelegationUtils { public static  I wrap(Class iface, I wrapped) { return wrapInternally(iface, wrapped, new SimpleDecorator(wrapped)); } private static  I wrapInternally (Class iface, I wrapped, InvocationHandler handler) { return (I) Proxy.newProxyInstance(wrapped.getClass().getClassLoader(), new Class[] { iface }, handler); } private static class SimpleDecorator implements InvocationHandler { private final T delegate; private SimpleDecorator(T delegate) { this.delegate = delegate; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Method m = findMethod(delegate.getClass(), method); if (m == null) { throw new NullPointerException("Found no method " + method + " in delegate: " + delegate); } return m.invoke(delegate, args); } } private static Method findMethod(Class clazz, Method method) throws Throwable { try { return clazz.getDeclaredMethod(method.getName(), method.getParameterTypes()); } catch (NoSuchMethodException e) { return null; } } } 

测试一下:

 public class Test { public interface Test { public void sayHello (); } public static class TestImpl implements Test { @Override public void sayHello() { System.out.println("HELLO!"); } } public static void main(String[] args) { Test proxy = DelegationUtils.wrap(Test.class, new TestImpl()); proxy.sayHello(); } } 

我想创建一个自动委托类,在EDT上执行委托方法。 使用此类,您只需创建一个将使用EDTDecorator的新实用程序方法,其中实现将m.invoke包装在SwingUtilities.invokeLater

但是,如果我反思这一点,我可能想重新考虑为每个接口创建一个非基于reflection的代理 – 它可能更干净,更快,更容易理解。 但是,这是可能的。