Implémentation de L'Interface générique en Java
J'ai une question Java generics que j'espérais que quelqu'un pourrait répondre. Considérez le code suivant:
public interface Event{}
public class AddressChanged implements Event{}
public class AddressDiscarded implements Event{}
public interface Handles<T extends Event>{
public void handle(T event);
}
Je veux implémenter cette interface de poignées comme ceci:
public class AddressHandler implements Handles<AddressChanged>, Handles<AddressDiscarded>{
public void handle(AddressChanged e){}
public void handle(AddressDiscarded e){}
}
Mais java ne permet pas d'implémenter des poignées deux fois en utilisant le générique. J'ai pu accomplir cela avec C#, mais je ne peux pas trouver une solution de contournement en java sans utiliser Reflection ou instanceof et casting.
Existe-t-il un moyen en java d'implémenter L'interface Handles en utilisant les deux interfaces génériques? Ou peut-être une autre façon d'écrire L'interface des poignées afin que le résultat final puisse être accompli?
7 réponses
Vous ne pouvez pas faire cela en Java. Vous ne pouvez implémenter qu'une réalisation concrète de la même interface générique. Je ferais cela à la place:
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){}
}
Aller après @Amir Raminfar, vous pouvez utiliser visiteur modèle
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){}
}
Non, car différents types génériques "concrets" en Java compilent le même type . L'interface réelle que votre objet implémentera est:
public interface Handles {
public void handle(Event event);
}
Et, évidemment, vous ne pouvez pas avoir deux méthodes différentes avec une signature identique...
AFAIK vous ne pouvez pas le faire, car lors de la compilation du code source en Java, ceux-ci se résorberont à handle(Event)
, rendant la méthode ambiguë.
Les informations génériques ne sont pas disponibles pendant L'exécution en Java, contrairement à C#. C'est pourquoi il fonctionne comme vous le décrivez.
Vous devrez changer les noms des méthodes pour les rendre uniques, comme handleAddressChanged
et handleAddressDiscarded
.
C'est en effet l'un des points faibles des génériques Java.
Malheureusement non. La solution habituelle (gras, laid, rapide) est de créer un Handles
interface (c'est à dire HandlesAddressChange
, HandlesAddressDiscarded
) et donner à chacun d'eux une méthode différente (handleAddressChange(...)
, handleAddressDiscarded()
).
De cette façon, le runtime Java peut les distinguer.
Ou vous pouvez utiliser des classes anonymes.
Ce N'est pas autorisé car Java efface les signatures génériques lors de la compilation. La méthode d'interface aura en fait la signature
public void handle(Object event);
Donc vous avez deux choix. Implémentez des gestionnaires distincts pour différents événements:
public class AddressChangedHandler implements Handles<AddressChanged>{ /* ... */ }
public class AddressDiscardedHandler implements Handles<AddressDiscarded>{ /* ... */ }
Ou implémentez un gestionnaire pour tous, mais vérifiez le type de l'événement entrant:
public void handle(Event e){
if (e instanceof AddressChanged) {
handleAdressChanged(e);
}
else if (e instanceof AddressDiscareded) {
handleAdressDiscarded(e);
}
}
Une implémentation comme celle-ci ne fonctionnera pas en raison des contraintes de la spécification java. Mais si vous n'avez pas peur d'utiliser AOP ou une sorte de conteneur IOC, vous pouvez utiliser des annotations pour cela. Que vos Aspects ou le conteneur pourraient gérer l'infrastructure de messagerie et appeler les méthodes que vous annotez.
Vous devez d'Abord créer les annotations.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EventConsumer {}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Handles{}
Vous pouvez annoter votre classe comme ça:
@EventConsumer
public class AddressHandler{
@Handles
public void handle(AddressChanged e){}
@Handles
public void handle(AddressDiscarded e){}
}