2010-12-01 11 views
23

Ich habe eine Java-Generika-Frage, die ich gehofft hatte, dass jemand antworten könnte. Betrachten Sie den folgenden Code ein:Generische Schnittstelle in Java implementieren

public class AddressHandler implements Handles<AddressChanged>, Handles<AddressDiscarded>{ 
    public void handle(AddressChanged e){} 
    public void handle(AddressDiscarded e){} 
} 

Aber Java erlaubt keine Griffe zweimal mit dem generischen Umsetzung:

public interface Event{} 
public class AddressChanged implements Event{} 
public class AddressDiscarded implements Event{} 

public interface Handles<T extends Event>{ 
    public void handle(T event); 
} 

ich diese Griffe Schnittstelle so implementieren möchten. Ich konnte dies mit C# erreichen, aber ich kann keine Workaround in Java finden, ohne Reflection oder instanceof und casting zu verwenden.

Gibt es eine Möglichkeit in Java, die Handles-Schnittstelle mit beiden generischen Schnittstellen zu implementieren? Oder vielleicht eine andere Möglichkeit, die Handles-Schnittstelle zu schreiben, damit das Endergebnis erreicht werden kann?

Antwort

9

Das ist in Java nicht möglich. Sie können nur eine konkrete Realisierung derselben generischen Schnittstelle implementieren. Ich würde dies tun, anstatt:

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){} 
} 
+4

Diese diskutierbar ist ... Diese "instanceof Kaskade" ist ein sehr verfahrens paragidm. Natürlich funktioniert es, aber IMHO ist es nicht sehr guter Stil. Aber auch unterschiedliche Methodennamen sind vielleicht nicht perfekt, es ist wahrscheinlich Geschmackssache. –

+1

Das würde ich tun. Sie müssen nicht :) –

+0

-1: Sie können mehrere Schnittstellen in Java implementieren. –

1

AFAIK Sie nicht tun können, dass, weil, wenn der Quellcode in Java kompilieren diese werden beide laufen auf handle(Event), wodurch das Verfahren nicht eindeutig.

Die generische Information ist während der Laufzeit in Java nicht verfügbar, im Gegensatz zu C#. Deshalb funktioniert es so, wie du es beschreibst.

Sie müssen die Methodennamen ändern, um sie eindeutig zu machen, wie handleAddressChanged und handleAddressDiscarded.

Dies ist in der Tat einer der Schwachpunkte der Java-Generika.

+0

Es ist wirklich ein Problem bei der Kompilierung. Sie müssen die Laufzeiteigenschaften der Objekte nicht kennen (das Argument könnte sogar "null" sein!), Um die Methoden zu unterscheiden. –

2

Nein, weil verschiedene "konkrete" generische Typen in Java zu des gleichen Typs kompilieren. Die eigentliche Schnittstelle Ihr Objekt implementieren ist:

public interface Handles { 
    public void handle(Event event); 
} 

Und, natürlich, nicht zwei verschiedene Methoden mit einer identischen Signatur haben kann ...

+0

Eigentlich ist die Löschung 'public void handle (Event event);' –

+0

@ S.P.Floyd: D'oh! Fest. – cdhowie

+0

Wie habe ich in meiner Antwort :-) –

17

Gehen nach @Amir Raminfar, können Sie visitor Muster verwenden

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){} 
} 
+0

+1, aber in dem Muster "akzeptieren" die Veranstaltung den Besucher, und der Besucher "besuchen" das Ereignis :) –

+0

@Sylvain M, Danke. Fest. –

+0

Event e hat keinen Besuch() – robev

0

Leider nicht. Die übliche Lösung (Fett, hässlich, schnell) besteht darin, eine Handles Schnittstelle (d. H. HandlesAddressChange, HandlesAddressDiscarded) zu erzeugen und jedem von ihnen eine andere Methode zu geben (handleAddressChange(...), handleAddressDiscarded()).

Auf diese Weise kann die Java-Laufzeit unterscheiden.

Oder Sie können anonyme Klassen verwenden.

0

Dies ist nicht zulässig, da Java generische Signaturen während der Kompilierung löscht. Die Schnittstellenmethode hat tatsächlich die Signatur

public void handle(Object event); 

Sie haben also zwei Möglichkeiten.Entweder implementieren getrennten Handlers für verschiedene Ereignisse:

public class AddressChangedHandler implements Handles<AddressChanged>{ /* ... */ } 
public class AddressDiscardedHandler implements Handles<AddressDiscarded>{ /* ... */ } 

oder implementieren einen Handler für alle, aber die Art des eingehenden Ereignisses überprüfen:

public void handle(Event e){ 
    if (e instanceof AddressChanged) { 
    handleAdressChanged(e); 
    } 
    else if (e instanceof AddressDiscareded) { 
    handleAdressDiscarded(e); 
    } 
} 
0

Eine Implementierung wie das wird nicht funktionieren aufgrund der Zwänge die Java-Spezifikation. Aber wenn Sie keine Angst haben, AOP oder eine Art von IOC-Container zu verwenden, könnten Sie Anmerkungen dafür verwenden. Dann könnten Ihre Aspekte oder der Container die Messaging-Infrastruktur verwalten und die von Ihnen kommentierten Methoden aufrufen.

Zuerst müssen Sie die Annotationen erstellen.

@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface EventConsumer {} 

@Target(ElementType.METHOD) 
@Retention(RetentionPolicy.RUNTIME) 
public @interface Handles{} 

Das können Sie Ihre Klasse wie das mit Anmerkungen versehen:

@EventConsumer 
public class AddressHandler{ 
    @Handles 
    public void handle(AddressChanged e){} 
    @Handles 
    public void handle(AddressDiscarded e){} 
} 
Verwandte Themen