2016-11-09 2 views
8

Je suis en train de trier List des employés par leur nom puis l'âge à l'aide Java8 Comparator, j'ai créé ci-dessous Comparator mais il me donne une erreur du compilateurcomparaison et thenComparing donne erreur de compilation

Type mismatch: cannot convert from Comparator<Object> to <unknown>

Comparator<String> c = Comparator.comparing(s -> s.split("\\s+")[0]) 
      .thenComparingInt(s -> Integer.parseInt(s.split("\\s+")[1])); //compile error 

mais cela fonctionne si je spécifie explicitement le Type

Comparator<String> c = Comparator.<String, String> comparing(s -> s.split("\\s+")[0]) 
      .thenComparingInt(s -> Integer.parseInt(s.split("\\s+")[1])); //works 

ou en créant deux Compartor s et de la chaîne

Comparator<String> name = Comparator.comparing(s -> s.split("\\s+")[0]); 
    Comparator<String> age = Comparator.comparingInt(s -> Integer.parseInt(s.split("\\s+")[1])); 
    Comparator<String> cc = name.thenComparing(age); //works 

J'ai spécifié le type Comparator<String> sur le côté gauche, mais pourquoi l'inférence de type automatique est de ne pas trouver le bon type et attend de spécifier explicitement.

Quelqu'un pourrait-il clarifier à ce sujet?

Voici le code

String[] arr = { "alan 25", "mario 30", "alan 19", "mario 25" }; 
    Comparator<String> c = Comparator.<String, String> comparing(s -> s.split("\\s+")[0]) 
      .thenComparingInt(s -> Integer.parseInt(s.split("\\s+")[1])); 
    List<String> sorted = Arrays.stream(arr).sorted(c).collect(Collectors.toList()); 
    System.out.println(sorted); 

sortie

[alan 19, alan 25, mario 25, mario 30] 
+0

Je ne suis pas un expert sur l'inférence de type générique, mais je devine que c'est juste « trop auto-inférence ». Probablement, il ne peut pas comprendre le type de la méthode 'comparison()' car il n'a aucun type à "ancrer" à, contrairement à la méthode de chaînage où vous donnez explicitement un type au résultat de 'comparaison () '. Quoi qu'il en soit, je pense qu'il serait plus lisible d'écrire votre propre 'comparateur ', et seulement d'appeler' split' une fois. Il n'y a pas de prix pour serrer le code dans le moins de lignes possibles. – ajb

+1

La saisie de cible ne fonctionne pas via les appels de méthode chaînés, voir [ici] (http://stackoverflow.com/a/28834656/2711488) et [ici] (http://stackoverflow.com/a/26883991/2711488) et [ici] (http://stackoverflow.com/a/31383947/2711488). Cependant, vous pouvez simplement utiliser 'Comparator.comparing (s -> s.replaceFirst (" \\ s + "," "))' à la place de votre comparateur à deux étages et le résultat sera le même ... – Holger

+0

'Comparator.comparing ((String s) -> s.split ("\\ s +") [0]). ThenComparingInt (s -> Integer.parseInt (s.split ("\\ s +") [1])) 'fonctionne mais pourquoi ce' String' est requis, il peut déduire de 'Comparator ', est-ce une limitation dans l'inférence de type? BTW en utilisant «version java» 1.8.0_60 "' – Saravana

Répondre

2

Java a besoin de connaître un type de toutes les variables. Dans de nombreux lambdas, il peut inférer un type, mais dans votre premier extrait de code, il ne peut pas deviner le type de s. Je pense que la méthode standard pour résoudre ce problème serait de déclarer explicitement:

Comparator<String> c = Comparator.comparing((String s) -> s.split("\\s+")[0]) 
      .thenComparingInt(s -> Integer.parseInt(s.split("\\s+")[1])); 

Si vous regardez this answer, il a une déclaration de type similaire dans l'argument Comparator.comparing().

Votre méthode, en donnant explicitement les arguments de type comparing(), fonctionne évidemment aussi.

Pour votre autre méthode, en déclarant deux comparateurs, je suis assez confiant que dans ce cas, Java peut déduire String sur le côté gauche de l'affectation, tout comme dans le List <String> = new ArrayList<>(); conventionnel. Lorsque vous appelez thenComparing() dans la même expression, Java ne peut plus voir que le type du côté gauche est pertinent. Ce serait un peu comme int size = new ArrayList<>().size(); Cela fonctionne aussi:

Comparator<String> name = Comparator.comparing(s -> s.split("\\s+")[0]); 
    Comparator<String> c = name.thenComparingInt(s -> Integer.parseInt(s.split("\\s+")[1])); 
+1

@ ole-vv Comparator.comparing ((String s) -> s.split ("\\ s +") [0]). ThenComparingInt (s -> Integer.parseInt (s.split ("\\ s +") [1])) fonctionne mais pourquoi cette chaîne est requis, il peut déduire du comparateur , est-ce une limitation dans l'inférence de type? BTW en utilisant la version java "1.8.0_60" – Saravana

+2

Comment pouvez-vous déduire cela? Vous savez que 'thenComparing()' devrait retourner un 'Comparateur ' (pour correspondre au côté gauche de l'assignation), par conséquent 'comparison()' doit retourner 'Comparator ', donc 's' doit être un 'String'. Le compilateur ne passe pas par ces nombreuses étapes de raisonnement. Oui, c'est une limitation dans l'inférence de type, mais je pense que c'est une limitation introduite à dessein parce que sinon les règles deviendraient trop compliquées et les programmeurs ne les comprendraient pas. C'est ma meilleure estimation. –

+1

Le plus gros problème est que la saisie de la cible par des invocations chaînées pourrait changer les méthodes disponibles pour l'invocation suivante, qui était en fait utilisée pour déterminer le type cible 'précédent, ie avec 'foo (x) .bar (y)', le retour Le type de 'foo (x)' détermine si et quelles méthodes 'bar (...)' sont disponibles, mais appliquer la frappe à 'foo (x)' dépend de la méthode 'bar (...)' choisie. – Holger