2017-10-21 88 views
1

J'ai besoin d'un comparateur pour une goyave MultiSet.Entry, pour trier d'abord par comptage et ensuite par chaîne. Cependant, j'ai des problèmes de compilateur et je pense que je reçois quelque chose qui ne va pas avec les génériques.Comparateur pour MultiSet.Entry et Generics

Cette compile:

Comparator<Multiset.Entry<String>> comparator() { 
    return Comparator.comparing(Multiset.Entry::getCount); 
} 

Cependant, cela ne:

Comparator<Multiset.Entry<String>> comparator() { 
    return Comparator.comparing(Multiset.Entry::getCount).thenComparing(Multiset.Entry::getElement); 
} 
 
Error: java: incompatible types: cannot infer type-variable(s) T,U 
    (argument mismatch; invalid method reference 
     method getCount in interface com.google.common.collect.Multiset.Entry cannot be applied to given types 
     required: no arguments 
     found: java.lang.Object 
     reason: actual and formal argument lists differ in length) 

Si je combine Comparateurs par exemple pour un String, je n'ai évidemment pas de problème similaire. Qu'est-ce que je manque ici, et comment puis-je le faire fonctionner?

(Note: Je suis conscient qu'il pourrait y avoir des approches différentes avec goyave, mais je veux comprendre le problème que je reçois ici.)

+1

Je ne prétends pas comprendre pourquoi c'est nécessaire, mais vous pouvez compiler ces chaînes 'thenComparing' en ajoutant des arguments de type explicites pour le premier appel de méthode. 'Comparateur. , Integer> comparaison (Multiset.Entry :: getCount) .thenComparing (Multiset.Entry :: getElement);' –

+3

Vous devez également utiliser comparisonInt() pour la première comparaison: 'return Comparator. < Multiset.Entry > comparisonInt (Multiset.Entry :: getCount) .thenComparing (Multiset.Entry :: getElement); ' –

+0

comparaisonInt() a du sens, mais nécessite toujours un argument de type explicite. Pourquoi est-il nécessaire avec MultiSet.Entry, mais pas avec String? –

Répondre

2

Je pense c'est à cause de la façon dont le type de ciblage (une forme de Java inferencing) fonctionne en conjonction avec la programmation de la fonctionnalité point utilisée dans votre code (vous pouvez lire à propos des types de cibles here dans les docs java, en particulier la section intitulée: Target Types and Method Arguments).

En très peu, si j'ai quelque chose comme ceci:

List<String> list = new List<>(); 

Le type de cible est ici List<String> si Java (8) se rend compte que la nouvelle liste que vous créez est attribué à et doit être de type List<String>, par conséquent le type peut être déduit et vous ne devez pas spécifier le paramètre de type.

Ce qu'il se résume à la première snippest qui fait la compilation est la suivante: La section de code Comparator.comparing(Multiset.Entry::getCount); renvoie un type de Comparator<T>, ce résultat est immédiatement affecté au type de retour de la méthode (à savoir le type de retour de la méthode est la cible type). Le compilateur peut déduire le type puisque le type cible est le type de retour de la méthode comparator, que vous avez explicitement défini/capturé dans la signature de la méthode comme Comparator<Multiset.Entry<String>>. Le T dans Comparator<T> peut donc être déduit.

Dans le deuxième extrait de code (qui ne compile pas et requiert l'argument témoin de type explicite - le bit explicite dans le <> pour l'appel de méthode), vous utilisez l'opérateur point qui enchaîne les méthodes. Cela signifie que la deuxième partie thenComparing(Multiset.Entry::getElement) est appliquée au résultat de la première partie Comparator.comparing(Multiset.Entry::getCount).

La deuxième partie peut déduire le type du type de cible, puisque le résultat de la deuxième partie est ce qui est retourné par la méthode, de sorte que le type cible de la seconde partie est similaire au cas précédent. Le type de retour de méthode a été capturé et défini, ainsi il peut en déduire ceci.

Mais pour la première partie, le type de cible est incertain, car il n'est pas affecté à un type avec des types réels. Selon l'API Comparator, Comparator<T> est ce qui est retourné par cette fonction, mais comme il ne sait pas à quel type il va être assigné spécifiquement, vous devez fournir le type de témoin et spécifier explicitement que lorsqu'il renvoie Comparator<T> le T devrait être (dans votre cas) Multiset.Entry<String>.

+0

P.s. Je ne connais pas grand-chose à la goyave, donc je ne sais pas si Guava a une influence sur le fonctionnement de l'inférence. – Smiley

+0

Ah, je n'étais évidemment pas au courant de toutes les implications de l'inférence de type en conjonction avec lambdas. Merci pour la réponse détaillée, et je suis sûr que cela n'a rien à voir avec la goyave en particulier. –

+0

Étant donné le message "les listes d'arguments réelles et formelles diffèrent en longueur", cela ne peut pas être correct. Au moins, je ne comprends pas d'où vient la liste d'arguments de taille erronée. – maaartinus