Jest to możliwe przy użyciu dynamicznego Proxy.
Najprostszym sposobem jest również przekazanie pożądanego interfejsu jako pierwszego parametru do Twojego połączenia Mux.create()
. W przeciwnym razie będziesz musiał użyć odbicia, aby spróbować odgadnąć pożądany interfejs ze wszystkich dostarczonych konkretnych instancji nasłuchiwania (trudne do określenia, czy wszystkie obiekty nasłuchiwania implementują kilka wspólnych interfejsów).
Oto krótka z nim:
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);
}
}
które należałoby użyć, na przykład, w następujący sposób:
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
Działa to po prostu tworząc dynamiczną Proxy
że odlewa się do typu docelowego T
. Ten serwer proxy używa numeru InvocationHandler
, który po prostu przekazuje wszystkie wywołania metod do serwera proxy w celu podania konkretnych instancji.
pamiętać, że w ogóle mogę sfinalizować wszystkie parametry i zmienne lokalne w miarę możliwości, tylko ja sfinalizowane T... instances
w tym przypadku zwrócić uwagę na fakt, że jeśli instances
był nie końcowy, a następnie przedstawieniu go w anonimowej klasy wewnętrznej nie będzie być dozwolone (otrzymasz "Nie można odwołać się do nieostatecznej zmiennej args wewnątrz klasy wewnętrznej zdefiniowanej w innej metodzie").
Należy również zauważyć, że powyższe założenie zakłada, że rzeczywiste wywołania metod nie zwracają żadnych znaczących (lub użytecznych) wartości, dlatego też zwraca również null
dla wszystkich wywołań metod. Będziesz musiał dodać trochę więcej kodu, jeśli chcesz zebrać wartości zwracane i zwrócić je również w znaczący sposób.
Alternatywnie, można kontrolować wszystkie podane instances
określenie popularne interfejsy wszyscy realizacji i przenieść wszystkie te, newProxyInstance()
. To sprawia, że Mux.create()
jest o wiele bardziej wygodny w użyciu, z powodu utraty kontroli nad jego zachowaniem.
/**
* @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);
}
Jeśli wszystkie słuchacze implementują interfejs "AppleListener", nie widzę potrzeby a) odbicia, ani b) generycznych. Po prostu weź je wszystkie i dodaj gdzieś do 'Listy' w twoim 'Mux' i powtórz. Czy może czegoś brakuje? –
Czy możesz wyjaśnić więcej na temat swojego multipleksera? Ponieważ to samo przyszło mi do głowy, jak powiedział Alistair Israel. –
Chociaż znajduje się nieco poza tym, do czego są przeznaczone, można użyć klasy [Proxy] (http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/Proxy.html). To tworzy obiekt, który wygląda jak dana klasa, ale wywołuje program obsługi, aby przetworzyć jego wywołania, a przewodnik może następnie iterować przez słuchaczy. – BevynQ