Wie generische Multitype-Sammlung generischer Handler deklariert wird

8

Ich habe es immer schwer, Generika mit Sammlungen und Platzhaltern zu verwenden.

Also hier ist die folgende Karte. Ich möchte die Sammlung von Handlern für einen bestimmten Pakettyptyp beibehalten.

%Vor%

Und das PacketListener

%Vor%

Nun möchte ich die Listener wie folgt von der eingehenden Paketklasse abhängig machen:

%Vor%

Und schließlich möchte ich einen solchen Aufruf durchführen

%Vor%

Alles, was ich bekomme, sind nur eine Menge Fehler. Liegt es an Platzhaltern in der Deklaration der Sammlung? Ist es möglich, eine solche Lösung zu erreichen?

    
Antoniossss 22.05.2015, 08:32
quelle

3 Antworten

4

Es gibt ein schönes Bild: In einer der anderen Antworten , die Ihnen dieses Problem erklären können.

Das Ding heißt PECS, was für

steht
  

Producer extends und Consumer super .

TL; DR: Sie können nur beide add und get von / zu einer Sammlung mit einem konkreten Typ ( T ). Sie können T (und seine möglichen Subtypen) mit T extends Something erhalten und Sie können Something zu einem Collection mit T super Something hinzufügen, aber Sie können nicht beide Wege gehen: also Ihre Fehler.

    
Adam Arold 22.05.2015 08:52
quelle
1

Ihr Problem beginnt hier:

%Vor%

Sie erwarten (oder vielleicht nur hoffen), dass Sie die beiden ? miteinander verbinden können, damit ein Lookup mit einem Schlüssel vom Typ Class<T> zu einem Wert vom Typ List<PacketListener<T>> führt. Leider gibt es keine Möglichkeit, Java mitzuteilen, dass die beiden ? gleich sind, aber unterschiedliche (aber eingeschränkte) Typen annehmen können.

Dieses Problem wird normalerweise mit den oben erwähnten covariance/contravariance -Methoden gelöst, aber in Ihrem Fall müssen Sie sowohl als auch aus Ihrer Sammlung lesen. Sie müssen daher ein invariance verwenden.

Ich glaube, eine Lösung für Ihr Problem besteht darin, die beiden Objekte in eine Hilfsklasse zu binden und so die Invarianz dort einzuführen. Auf diese Weise können Sie ihre Gleichheit beibehalten und sie trotzdem unter Einschränkungen variieren lassen.

Einige davon sind ein bisschen hacky IMHO (d. h. es gibt einige Casts), aber zumindest können Sie Ihr Ziel erreichen und Sie sind immer noch Typ sicher. Die Modelle sind nachweisbar gültig.

%Vor%

Beachten Sie, dass, obwohl allgemein angenommen wird, dass Sie etwas falsch machen, wenn Sie etwas während der Verwendung von Generika umsetzen müssen - in diesem Fall können wir aufgrund der Laufzeit sicher sein, dass alle Listeners<T> Objekte in der Map werden durch ihre Class<T> kodiert und daher ist die eingeschlossene Liste tatsächlich ein List<PacketListener<T> .

    
OldCurmudgeon 22.05.2015 10:26
quelle
0

Das Folgende ähnelt der Antwort von @OldCurmudgeon.

Der Schlüsselpunkt ist auch das Feld listeners . Aber ich erkläre es so:

%Vor%

Der Punkt hier ist, dass wir die Liste als Map-Wert-Typ loswerden. DelegatingPacketListener wird wie folgt deklariert:

%Vor%

Nun, da DelegatingPacketListener nur Listener vom Typ Packet unterstützt, benötigen wir eine spezifischere Implementierung von PacketListener :

%Vor%

Bitte beachten Sie, dass der Typparameter T in der implements -Klausel nicht verwendet wird. Es ist nur für die Implementierung verwendet. Wir werden jedes PacketListener , das an die API übergeben wurde, in ein WrappingPacketListener einbinden. Die Implementierung ist also so:

%Vor%

Die API hat sich leicht geändert für getPacketListeners , die keinen generischen Typ mehr verwendet.

Im Vergleich mit der Lösung von OldCurmudgeon bleibt diese an der bereits vorhandenen Schnittstelle PacketListener haften und erfordert keine ungeprüfte Umwandlung.

Beachten Sie, dass die Implementierung nicht threadsicher ist, da die Implementierung von addPacketListener eine Synchronisation auf dem Kartenschlüssel erfordert (wie der ursprüngliche Code auch benötigt). Die Kapselung der Liste der Paketlistener in immutable DelegatingPacketListener ist jedoch wahrscheinlich besser für den Parallelbetrieb geeignet.

    
SpaceTrucker 22.05.2015 11:56
quelle

Tags und Links