2017-05-29 7 views
0

Etant donné un type générique Result<T> avec la mise en œuvre partielle suivanteJava - entrée générique Délimitée à méthode générique limitée

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 */ 

} 

et l'utilisation de celui-ci dans un visiteur ...

Interface:

public interface Visitor<T> { 

    T visitFile(FileContext ctx); 

    T aggregate(T left, T right); 

    /* ... */ 

} 

Mise en œuvre:

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 de SomeVisitor:

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

L'exemple ci-dessus échoue avec les messages d'erreur de type discordance donné.

J'ai déjà essayé de changer la signature de .mergeWith pour accepter le beaucoup plus étroit Result<T> au lieu d'un Result<? extends T>. Cela conduit à un type attendu de Result<capture of ? extends Node> dans cet exemple. Et le casser dans d'autres endroits, où <? extends T> est le type générique correct, puisque other est un producteur dans ce cas.

La seule solution qui fonctionne réellement, est renversons les deux left et right-Result<Node>, les fusionner, puis retourner:

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

Non seulement est que laid et introduit des variables temporaires, il est également de ne pas obtenir plus jolie quand Je joins les moulages.

Je voudrais savoir si c'est juste la vérité laide sur les génériques Java et la limite de ceux-ci ou si je fais quelque chose de mal.

+0

Pouvez-vous afficher la signature 'Visitor.visitFile()'? En outre, cela aiderait à montrer comment 'SomeVisitor' est réellement utilisé. – shmosel

+0

@shmosel fait. J'espère que cela aide. – baeda

Répondre

0

Selon le résultat se confondent déclaration vous pouvez fusionner un résultat <S> avec un résultat <T> si et seulement si T est un sous-type de S.

Dans votre méthode globale, le paramètre de type de gauche est une sous-classe inconnue de nœud, et le paramètre de type right est également une sous-classe inconnue de Node. Il n'y a aucune garantie que le paramètre type de right est une sous-classe de left.

Que faire si vous essayez ceci:

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; 
} 

Ici, nous déclarons deux types S et T et exiger que S est un sous-type de nœud et T est un sous-type de S.

+0

Un moyen très astucieux (mais très Java-y) de contourner ce problème. Malheureusement, l'interface que je suis en train de mettre en œuvre décrit l'agrégat comme un agrégat T (T left, T right); 'Peut-être que je devrais augmenter la question avec cette info :) Désolé 'bout de ça! – baeda

+0

Je ne sais pas si c'est possible sans lancer ...Cependant, il semble que vous ayez seulement besoin de lancer une seule fois. tempLeft.mergeWith (à droite) devrait être valide. –

0

Le problème vient à partir des captures ?, qui sont indépendantes les unes des autres. N'utilisez pas les captures ? dans les définitions de classe. Utiliser des identificateurs de type générique:

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

'implements Visitor >' ne compile pas, car les limites d'un identificateur de type générique doivent être définies sur la classe d'implémentation, pas sur l'interface. – baeda

+0

oups. Je vais arranger ça. J'avais l'intention de déclarer la classe comme 'SomeVisitor ' –