可以使用动态Proxy。
最简单的方法是将所需的接口作为第一个参数传递给您的Mux.create()
调用。否则,您将不得不使用反射来尝试从提供的所有具体侦听器实例中猜测所需的接口(很难确定所有侦听器对象是否都实现了几个共同的接口)。
这是它的简短之处:
public class Mux {
/**
* @param targetInterface
* the interface to create a proxy for
* @param instances
* the concrete instances to delegate to
* @return a proxy that'll delegate to all the arguments
*/
@SuppressWarnings("unchecked")
public static <T> T create(Class<T> targetInterface, final T... instances) {
ClassLoader classLoader = targetInterface.getClassLoader();
InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method m, Object[] args)
throws Throwable {
for (T instance : instances) {
m.invoke(instance, args);
}
return null;
}
};
return (T) Proxy.newProxyInstance(classLoader,
new Class<?>[] { targetInterface }, handler);
}
}
您将使用它,例如,如下所示:
Apple apple = new Apple();
AppleListener l1 = new AppleListenerA();
AppleListener l2 = new AppleListenerB();
apple.setListener(Mux.create(AppleListener.class, l1, l2));
apple.doSomething(); // will notify all listeners
这通过简单地创建一个Proxy
转换为目标类型的动态来工作T
。该代理使用InvocationHandler
仅将代理的所有方法调用委托给给定的具体实例。
请注意,虽然一般情况下我会尽可能地完成所有参数和局部变量,但我只T... instances
在这种情况下完成了这样一个事实,即如果instances
不是最终的,则不允许在匿名内部类中引用它(你会得到一个“不能在以不同方法定义的内部类中引用非最终变量 args”)。
另请注意,以上假设实际方法调用不返回任何有意义(或有用)的值,因此所有方法调用handler
也返回null
。如果您想收集返回值并以有意义的方式返回这些值,则需要添加更多代码。
或者,可以检查所有给定instances
的接口以确定它们都实现的通用接口,并将所有这些传递给newProxyInstance()
. 这使得Mux.create()
使用起来更加方便,但失去了对其行为的一些控制。
/**
* @param instances
* the arguments
* @return a proxy that'll delegate to all the arguments
*/
@SuppressWarnings("unchecked")
public static <T> T create(final T... instances) {
// Inspect common interfaces
final Set<Class<?>> commonInterfaces = new HashSet<Class<?>>();
commonInterfaces.addAll(Arrays.asList(instances[0].getClass()
.getInterfaces()));
// Or skip instances[0]
for (final T instance : instances) {
commonInterfaces.retainAll(Arrays.asList(instance.getClass()
.getInterfaces()));
}
// Or use ClassLoader.getSystemClassLoader();
final ClassLoader classLoader = instances[0].getClass().getClassLoader();
// magic
final InvocationHandler handler = new InvocationHandler() {
@Override
public Object invoke(final Object proxy, final Method m, final Object[] args)
throws Throwable {
for (final T instance : instances) {
m.invoke(instance, args);
}
return null;
}
};
final Class<?>[] targetInterfaces = commonInterfaces
.toArray(new Class<?>[commonInterfaces.size()]);
return (T) Proxy.newProxyInstance(classLoader, targetInterfaces,
handler);
}