2016-06-01 9 views
3

Ich kann nicht herausfinden, warum ich ein selbst-referenzierendes Generikum nicht werfen kann.Selbstreferenzierende Generics in Java

In Java habe ich ein sich selbst verweisendes Generikum. Es gibt eine Reihe von Dinge (Intent s) und Strategien zum Nachschlagen (Auflösen) dieser Dinge (ResolutionStrategy s).

Der selbstreferenzierende Typ Intent ist unten definiert. Ich möchte zur Kompilierzeit Klassen definieren, die nur eine ResolutionStrategy empfangen können, die die gleiche Absicht akzeptiert.

public interface Intent<I extends Intent<I, R>, R extends Resolution> 
{ 
    void resolve(ResolutionStrategy<I, R> strategy); 

    R getResolution(); 
} 

Resolution Strategie ist somit:

public interface ResolutionStrategy<I extends Intent<I, R>, R extends Resolution> 
{ 
    R resolve(I intent); 
} 

Also, wenn ich auf eine Liste dieser Intent s arbeitet, ich weiß nicht wirklich, was sie sind. Ich möchte jedoch bestimmte Typen erstellen, die eine konkrete Sache in meinem Domänenmodell darstellen. Hier ein Beispiel:

public class OrgIntent implements Intent<OrgIntent, IdentifiableResolution> 
{ 
    public final String name; 

    public OrgIntent(String name) 
    { 
     this.name = name; 
    } 

    @Override 
    public void resolve(ResolutionStrategy<OrgIntent, IdentifiableResolution> strategy) 
    { 
     // Do stuff 
    } 

    @Override 
    public IdentifiableResolution getResolution() 
    { 
     //Return resolution got from strategy at some point in the past 
     return null; 
    } 
} 

IdentifiableResolution ist eine einfache und uninteressant Implementierung von Resolution.

Alles gut so weit. Der Plan ist dann, ein nettes Diagramm dieser Intent s zu bauen, dann über sie iterieren und jedes an eine ResolutionStrategyFactory übergeben, um die relevante Strategie für das Lösen von ihnen zu erhalten. Allerdings kann ich nicht auf etwas Generisches anwenden, das zu einer Liste hinzugefügt werden kann!

private <I extends Intent<I, R>, R extends Resolution> DirectedAcyclicGraph<Intent<I, R>, DefaultEdge> buildGraph(Declaration declaration) throws CycleFoundException 
{ 
     DirectedAcyclicGraph<Intent<I, R>, DefaultEdge> dag = new DirectedAcyclicGraph<>(DefaultEdge.class); 
     // Does not compile 
     Intent<I, R> orgIntent = new OrgIntent("some name"); 
     // Compiles, but then not a valid argument to dag.addVertex() 
     Intent<OrgIntent, IdentifiableResolution> orgIntent = new OrgIntent("some name"); 
     // Compiles, but then not a valid argument to dag.addVertex() 
     OrgIntent orgIntent = new OrgIntent("some name"); 

     //Then do this 
     dag.addVertex(orgIntent); 
     ... 

Irgendwelche Ideen, was ich orgIntent als erklären sollte?

aktualisieren

Dank ich @zapl den generischen Typparameter auf der Methodendefinition realisiert war ein voller roter Hering.

Dies kompiliert, aber vermutlich bedeutet, ich könnte irgendwie eine Intent haben, die generalisiert ist, um irgendeinen alten Unsinn als den ersten generischen Typ zu haben?

+0

''? Ich sehe nicht, wie es zu, was auch immer in Beziehung gesetzt werden würde 'I' und' R' sind im Code – zapl

+0

'I' ist eine Implementierung von' Intent' und ' R' ist eine Implementierung der Resolution'. 'OrgIntent' implementiert' Intent 'und erfüllt damit' Intent '. –

+0

In Ihrer Zeile, wo Sie versuchen, das 'OrgIntent' zu konstruieren, funktioniert es, wenn Sie es in' OrgIntent ("irgendein Name") '' ändern? –

Antwort

1

Wie Zapl in den Kommentaren empfiehlt, bieten Generika nicht stark genug Typgarantien, um das Muster zu behandeln, das Sie beschreiben. Insbesondere weil Java-Generika nicht-reformiert sind, gibt es für die JVM keine Möglichkeit, einen spezifischeren Typ (OrgIntent) wiederherzustellen, nachdem sie in einen allgemeineren Typ umgewandelt wurde(). Da die generische Typinformation zur Laufzeit verloren geht, kann sich die JVM nur auf die konkreten Rohtypen verlassen (Intent).

Dies ist der gleiche Grund, zum Beispiel, dass Sie nicht zwei Methoden mit unterschiedlichen generischen Signaturen aber die gleichen konkreten Signatur definieren können - foo(List<String>) und foo(List<Integer>) beide einfach foo(List) zur Laufzeit wurden und wird nicht zulassen, damit die Compiler SieFormal Definieren Sie zwei solche Methoden in derselben Klasse.

Grob gesagt (und ich fürchte, ich verstehe Ihren Anwendungsfall nicht gut genug, um genauer zu sein) ist die Lösung, Objekte explizit mit dem gewünschten generischen Typ entweder über die zugehörige Class object oder TypeToken zu assoziieren.Zum Beispiel könnten Sie in der Lage sein, die folgende Signatur zur Arbeit zu kommen:

R resolve(Class<I> intentClass, I intent); 

Die in Effective Java Item 29: Consider typesafe heterogeneous containers angebotenen Beratung auch sollten hilfreich sein:

Manchmal jedoch mehr Flexibilität benötigen [als ein festen Anzahl der Typparameter] .... Die Idee ist, den Schlüssel anstelle des Containers zu parametrisieren. Zeigen Sie dann dem Container den parametrisierten Schlüssel, um einen Wert einzufügen oder abzurufen. Das generische Typsystem wird verwendet, um zu garantieren, dass der Typ des Wertes mit seinem Schlüssel übereinstimmt.

...

Java-Typ-System ist nicht stark genug, [die Art Beziehung zwischen Schlüsseln und Werten] zum Ausdruck bringen. Aber wir wissen, dass es stimmt, und wir nutzen es aus, wenn es darum geht, einen Favoriten zu finden.