web-dev-qa-db-fra.com

Comparator.reversed () ne compile pas avec lambda

J'ai une liste avec quelques objets utilisateur et j'essaye de trier la liste, mais ne fonctionne qu'avec la référence de méthode, avec l'expression lambda, le compilateur donne une erreur:

List<User> userList = Arrays.asList(u1, u2, u3);
userList.sort(Comparator.comparing(u -> u.getName())); // works
userList.sort(Comparator.comparing(User::getName).reversed()); // works
userList.sort(Comparator.comparing(u -> u.getName()).reversed()); // Compiler error

Erreur:

com\Java8\collectionapi\CollectionTest.Java:35: error: cannot find symbol
            userList.sort(Comparator.comparing(u -> u.getName()).reversed());
                                                     ^
symbol:   method getName()
location: variable u of type Object
1 error
98
Andrey

C'est une faiblesse du mécanisme d'inférence de type du compilateur. Afin de déduire le type de u dans le lambda, le type de cible pour le lambda doit être établi. Ceci est accompli comme suit. userList.sort() attend un argument de type Comparator<User>. Dans la première ligne, Comparator.comparing() doit renvoyer Comparator<User>. Cela implique que Comparator.comparing() a besoin d'un Function qui prend un argument User. Ainsi, dans le lambda de la première ligne, u doit être de type User et tout fonctionne.

Aux deuxième et troisième lignes, le typage cible est perturbé par la présence de l'appel à reversed(). Je ne sais pas trop pourquoi; Le destinataire et le type de retour de reversed() sont tous deux Comparator<T>. Il semble donc que le type de cible devrait être propagé au destinataire, mais ce n'est pas le cas. (Comme je l'ai dit, c'est une faiblesse.)

Dans la deuxième ligne, la référence de la méthode fournit des informations de type supplémentaires qui comblent cette lacune. Cette information étant absente de la troisième ligne, le compilateur déduit donc que u est Object (le repli d'inférence de dernier recours), ce qui échoue.

Évidemment, si vous pouvez utiliser une référence de méthode, faites-le et cela fonctionnera. Parfois, vous ne pouvez pas utiliser une référence de méthode, par exemple si vous voulez passer un paramètre supplémentaire, vous devez donc utiliser une expression lambda. Dans ce cas, vous fourniriez un type de paramètre explicite dans le lambda:

userList.sort(Comparator.comparing((User u) -> u.getName()).reversed());

Il est possible que le compilateur soit amélioré pour couvrir ce cas dans une prochaine version.

121
Stuart Marks

Vous pouvez contourner cette limitation en utilisant le double argument Comparator.comparing Avec Comparator.reverseOrder() comme second argument:

users.sort(comparing(User::getName, reverseOrder()));
78
Misha