Quanto segue è una sorta di simile alla risposta di @OldCurmudgeon.
Il punto chiave è anche il campo listeners
. Ma dichiaro come questo:
private final Map<Class<?>, DelegatingPacketListener> listeners
Il punto qui è che ci liberiamo della lista, come il tipo di mappa di valore. DelegatingPacketListener
è dichiarato come segue:
public class DelegatingPacketListener implements PacketListener<Packet> {
private final List<PacketListener<Packet>> packetListeners;
public DelegatingPacketListener(List<? extends PacketListener<Packet>> packetListeners) {
super();
this.packetListeners = new ArrayList<PacketListener<Packet>>(packetListeners);
}
@Override
public void onOutgoingPacket(Streamer streamer, Packet packet) {
for(PacketListener<Packet> packetListener : packetListeners) {
packetListener.onOutgoingPacket(streamer, packet);
}
}
@Override
public void onIncomingPacket(Streamer streamer, Packet packet) {
for(PacketListener<Packet> packetListener : packetListeners) {
packetListener.onIncomingPacket(streamer, packet);
}
}
public List<PacketListener<Packet>> getPacketListeners() {
return Collections.unmodifiableList(packetListeners);
}
}
Ora che DelegatingPacketListener
supporta solo gli ascoltatori di tipo Packet
abbiamo bisogno di un'altra specifica implementazione di PacketListener
:
public class WrappingPacketListener<T extends Packet> implements PacketListener<Packet> {
private final Class<T> packetClass;
private final PacketListener<T> wrapped;
public WrappingPacketListener(Class<T> packetClass, PacketListener<T> delegate) {
super();
this.packetClass = packetClass;
this.wrapped = delegate;
}
@Override
public void onOutgoingPacket(Streamer streamer, Packet packet) {
if(packetClass.isInstance(packet)) {
T genericPacket = packetClass.cast(packet);
wrapped.onOutgoingPacket(streamer, genericPacket);
}
}
@Override
public void onIncomingPacket(Streamer streamer, Packet packet) {
if(packetClass.isInstance(packet)) {
T genericPacket = packetClass.cast(packet);
wrapped.onIncomingPacket(streamer, genericPacket);
}
}
}
prega di notare che il parametro di tipo T
non viene utilizzato nel clausola sugli strumenti. È solo per l'implementazione utilizzata. Copieremo ogni PacketListener
passato all'API in un WrappingPacketListener
. Quindi l'implementazione è in questo modo:
public List<PacketListener<Packet>> getPacketListeners(Class<?> clazz) {
return Collections.<PacketListener<Packet>>singletonList(listeners.get(clazz));
}
public <T extends Packet> void addPacketListener(Class<T> clazz, PacketListener<T> listener) {
if (listeners.containsKey(clazz) == false) {
listeners.put(clazz, new DelegatingPacketListener(Collections.singletonList(new WrappingPacketListener<T>(clazz, listener))));
return;
}
DelegatingPacketListener existing = listeners.get(clazz);
List<PacketListener<Packet>> newListeners = new ArrayList<PacketListener<Packet>>(existing.getPacketListeners());
newListeners.add(new WrappingPacketListener<T>(clazz, listener));
listeners.put(clazz, new DelegatingPacketListener(newListeners));
}
private <T extends Packet> void notifyListeners(T packet) {
List<PacketListener<Packet>> listeners = streamer.getPacketListeners(packet.getClass());
if (listeners != null) {
for (PacketListener<Packet> packetListener : listeners) {
packetListener.onIncomingPacket(streamer, packet);
}
}
}
L'API è leggermente cambiata per getPacketListeners
che pretende molto più usare un tipo generico.
Rispetto alla soluzione di OldCurmudgeon, questo si integra con l'interfaccia PacketListener
già esistente e non richiede l'applicazione di un cast non controllato.
Si noti che l'implementazione non è thread-safe, poiché l'implementazione di addPacketListener
richiede la sincronizzazione sulla chiave della mappa (poiché anche il codice originale in questione ne ha bisogno). Tuttavia, incapsulare l'elenco dei listener di pacchetti in immutable DelegatingPacketListener
è probabilmente più adatto per scopi di concorrenza.
Provare a seguire il principio PECS. Renderà le tue cose facili. http://stackoverflow.com/questions/2723397/java-generics-what-is-pecs – Pranalee
Si prega di notare che anche se si utilizza un 'ConcurrentMap' il codice in' addPacketListener' non è thread-safe e richiederebbe la sincronizzazione sulla mappa chiave. – SpaceTrucker
@SpaceTrucker perché in realtà? – Antoniossss