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?

24
demandé sur Marijn 2010-12-01 18:36:48

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){}
}
9
répondu Amir Raminfar 2010-12-01 16:09:33

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){}
}
17
répondu Stan Kurilin 2010-12-01 20:12:11

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...

2
répondu cdhowie 2010-12-01 15:50:36

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.

1
répondu Daniel Schneller 2010-12-01 15:52:05

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.

0
répondu Aaron Digulla 2010-12-01 15:46:11

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);
  }
}
0
répondu Andreas_D 2010-12-01 15:48:03

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){}
}
0
répondu Olivier Heidemann 2010-12-03 23:28:25