Implementazione interfaccia generica in Java
Domanda
Ho domanda un Java generici Speravo che qualcuno potesse rispondere. Si consideri il seguente codice:
public interface Event{}
public class AddressChanged implements Event{}
public class AddressDiscarded implements Event{}
public interface Handles<T extends Event>{
public void handle(T event);
}
Voglio realizzare questa interfaccia maniglie in questo modo:
public class AddressHandler implements Handles<AddressChanged>, Handles<AddressDiscarded>{
public void handle(AddressChanged e){}
public void handle(AddressDiscarded e){}
}
Ma Java non consente l'attuazione Maniglie due volte con il generico. Sono stato in grado di realizzare questo con C #, ma non riesco a capire una soluzione in Java senza l'utilizzo di riflessione o instanceof e la colata.
C'è un modo in Java per implementare le maniglie interfaccia utilizzando entrambe le interfacce generiche? O forse un altro modo di scrivere le maniglie interfaccia in modo che il risultato finale può essere realizzato?
Soluzione
Non si può farlo in Java. È possibile implementare solo una realizzazione concreta della stessa interfaccia generica. Vorrei fare questo, invece:
public class AddressHandler implements Handles<Event>{
public void handle(Event e){
if(e instanceof AddressDiscarded){
handleDiscarded(e);
} else if(e instanceof AddressChanged){
handleChanged(e);
}
}
public void handleDiscarded(AddressDiscarded e){}
public void handleChanged(AddressChanged e){}
}
Altri suggerimenti
Andando dopo @Amir Raminfar, è possibile utilizzare visitatore modello
interface Event{
void accept(Visitor v);
}
interface Visitor {
void visitAddressChanged(AddressChanged a);
void visitAddressDiscarded(AddressDiscarded a);
}
class AddressChanged implements Event{
@Override
public void accept(Visitor v) {
v.visitAddressChanged(this);
}
}
class AddressDiscarded implements Event{
@Override
public void accept(Visitor v) {
v.visitAddressDiscarded(this);
}
}
class AddressHandler implements Visitor {
void handle(Event e){
e.accept(this);
}
public void visitAddressChanged(AddressChanged e){}
public void visitAddressDiscarded(AddressDiscarded e){}
}
No, perché diversi "concrete" tipi generici in Java compilazione di dello stesso tipo . L'interfaccia reale l'oggetto implementerà è:
public interface Handles {
public void handle(Event event);
}
E, ovviamente, non è possibile avere due metodi diversi, con una firma identica ...
Per quanto ne so non è possibile farlo, perché quando si compila il codice sorgente Java in questi saranno entrambi riducono a handle(Event)
, rendendo il metodo ambiguo.
Le informazioni generiche non è disponibile durante il runtime in Java, a differenza di C #. Ecco perché ci funziona come si descrive.
Si dovrà cambiare i nomi dei metodi per renderli unici, come handleAddressChanged
e handleAddressDiscarded
.
Questo è davvero uno dei punti deboli di generici Java.
Purtroppo no. La soluzione più comune (grasso, brutto, veloce) è creare un'interfaccia Handles
(cioè HandlesAddressChange
, HandlesAddressDiscarded
) e assegna a ciascuno di essi un metodo differente (handleAddressChange(...)
, handleAddressDiscarded()
).
In questo modo, il runtime Java può dire loro a parte.
In alternativa, è possibile utilizzare le classi anonime.
Non è consentito perché Java cancella firme generiche durante la compilazione. Il metodo di interfaccia sarà effettivamente avere la firma
public void handle(Object event);
Quindi, avete due scelte. O implementare gestori separati per i diversi eventi:
public class AddressChangedHandler implements Handles<AddressChanged>{ /* ... */ }
public class AddressDiscardedHandler implements Handles<AddressDiscarded>{ /* ... */ }
o implementare un gestore per tutti, ma controllare il tipo di evento in entrata:
public void handle(Event e){
if (e instanceof AddressChanged) {
handleAdressChanged(e);
}
else if (e instanceof AddressDiscareded) {
handleAdressDiscarded(e);
}
}
Un'implementazione come questo non funzionerà a causa dei vincoli della specifica Java. Ma se non hai paura di usare AOP o una sorta di un CIO-Container è possibile utilizzare le annotazioni per questo. Che i vostri aspetti o il contenitore potrebbe gestire l'infrastruttura di messaggistica e chiamare i metodi di annotare.
In primo luogo è necessario creare le annotazioni.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EventConsumer {}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Handles{}
La si può annotare la classe del genere:
@EventConsumer
public class AddressHandler{
@Handles
public void handle(AddressChanged e){}
@Handles
public void handle(AddressDiscarded e){}
}