2017-05-29 2 views
0

eine generische Art Result<T> mit der folgenden Teil Umsetzung BeiJava - generic Bounded als Eingabe begrenzt generische Methode

public class Result<T> { 

    /* fields, ctor, etc... */ 

    public Result<T> mergeWith(Result<? extends T> other) { 
     /* fiddle with this and other (acting as producer) and return */ 
    } 

    /* other methods */ 

} 

und der Verwendung in einem Visitor ...

Schnittstelle:

public interface Visitor<T> { 

    T visitFile(FileContext ctx); 

    T aggregate(T left, T right); 

    /* ... */ 

} 

Umsetzung:

public class SomeVisitor implements Visitor<Result<? extends Node>> { 

    // this works fine, as I can return subtypes of 'Node' 
    @Override 
    public Result<FileNode> visitFile() { } 

    /* ... */ 

    @Override 
    public Result<? extends Node> aggregate(Result<? extends Node> left, 
               Result<? extends Node> right) { 
     // This line completely blows up. 
     Result<? extends Node> tempResult = left.mergeWith(right); 
     // Expected type: 'Option<? extends capture of ? extends Node>' 
     // Have type:  'Option<      ? extends Node>' 
    } 

Callsite von SomeVisitor:

FileContext fileCtx = someOtherService.acquireFileContext(); 
SomeVisitor visitor = new SomeVisitor(); 
Result<FileNode> fileNodeResult = visitor.visitFile(fileCtx); 
// process the result 

Das obige Beispiel nicht mit den gegebenen Typ-Mismatch-Fehlermeldungen.

Ich habe bereits versucht, die Signatur von .mergeWith zu ändern, um die viel schmalere Result<T> statt einer Result<? extends T> zu akzeptieren. Dies führt zu einem erwarteten Typ von Result<capture of ? extends Node> in diesem Beispiel. Und breche es an anderen Stellen, wo <? extends T> der richtige generische Typ ist, da in diesem Fall other ein Produzent ist.

Die einzige Lösung, die tatsächlich funktioniert, wirft sowohl left nach unten und right-Result<Node>, sie verschmilzt und dann zurück:

@SuppressWarnings("unchecked") 
Result<Node> tempLeft = (Result<Node>) left; 
@SuppressWarnings("unchecked") 
Result<Node> tempRight = (Result<Node>) right; 
Result<Node> tempResult = tempLeft.mergeWith(tempRight); 

Das ist nicht nur hässlich und temporäre Variablen einführt, ist es auch nicht schöner bekommen, wenn Ich inline die Casts.

Ich würde gerne wissen, ob das ist nur die hässliche Wahrheit über Java-Generika und die Grenze davon oder wenn ich etwas falsch mache.

+0

Können Sie die 'Visitor.visitFile()' Signatur anzeigen? Es würde auch helfen zu zeigen, wie 'SomeVisitor' tatsächlich benutzt wird. – shmosel

+0

@smossel erledigt. Ich hoffe es hilft. – baeda

Antwort

0

Nach der zur Treffer Erklärung fusionieren Sie ein Ergebnis <S> mit einem Ergebnis zusammenführen können <T>, wenn und nur wenn T ein Subtyp von S.

In Ihrem Aggregat Methode ist, Typ-Parameter bleibt, ist eine unbekannte Unterklasse von Node, und der Typparameter von right ist auch eine unbekannte Unterklasse von Node. Es gibt keine Garantie dafür, dass der Typparameter des richtigen Typs eine Unterklasse der Linken ist.

Was passiert, wenn Sie versuchen, diese:

public <S extends Node, T extends S> Result<? extends Node> aggregate(Result<S> left, Result<T> right) { 
    Result<? extends Node> temp = left.mergeWith(right); 
    /** do stuff*/ 
    return temp; 
} 

Hier erklären wir zwei Typen S und T und verlangen, dass S ein Subtyp von Knoten und T ist ein Subtyp von S.

+0

Eine ziemlich nette (aber sehr Java-y) Art und Weise, um dies zu umgehen. Leider beschreibt die Schnittstelle, die ich implementiere, Aggregat als 'T-Aggregat (T links, T rechts);' Vielleicht sollte ich die Frage mit dieser Info ergänzen :) Sorry '! – baeda

+0

Nicht sicher, ob dies ohne Gießen möglich ist ...Es sieht jedoch so aus, als ob Sie nur einmal casten müssen. tempLeft.mergeWith (rechts) sollte gültig sein. –

0

Das Problem kommt, von den ? Aufnahmen, die unabhängig voneinander sind. Verwenden Sie keine ? Captures in Klassendefinitionen. Verwenden Sie generische Typenbezeichner:

public class SomeVisitor<T extends Node> implements Visitor<Result<T>> { 

    public Result<FileNode> visitFile() { } 

    /* ... */ 

    protected Result<T> aggregate(Result<T> left, Result<T> right) { 
     Result<T> tempResult = left.mergeWith(right); 
    } 
} 
+0

'implementiert Visitor >' kompiliert nicht, da die Grenzen eines generischen Typbezeichners in der implementierenden Klasse und nicht in der Schnittstelle definiert sein müssen. – baeda

+0

oops. Ich werde das reparieren. Ich wollte die Klasse als 'SomeVisitor ' deklarieren –

Verwandte Themen