Comparator.reversed() no se compila usando lambda

Resuelto Andrey asked hace 10 años • 4 respuestas

Tengo una lista con algunos objetos de Usuario y estoy tratando de ordenar la lista, pero solo funciona usando la referencia del método, con la expresión lambda el compilador da un error:

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

Error:

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
Andrey avatar Aug 07 '14 07:08 Andrey
Aceptado

Esta es una debilidad en el mecanismo de inferencia de tipos del compilador. Para inferir el tipo de ulambda, es necesario establecer el tipo de destino de la lambda. Esto se consigue de la siguiente manera. userList.sort()está esperando un argumento de tipo Comparator<User>. En la primera línea, Comparator.comparing()debe regresar Comparator<User>. Esto implica que Comparator.comparing()se necesita Functionun Userargumento. Así, en la lambda de la primera línea, udebe ser de tipo Usery todo funciona.

En la segunda y tercera línea, la escritura del objetivo se ve interrumpida por la presencia de la llamada a reversed(). No estoy del todo seguro de por qué; tanto el receptor como el tipo de retorno lo reversed()son Comparator<T>, por lo que parece que el tipo de destino debería propagarse de regreso al receptor, pero no es así. (Como dije, es una debilidad).

En la segunda línea, la referencia del método proporciona información de tipo adicional que llena este vacío. Esta información está ausente en la tercera línea, por lo que el compilador infiere uque es Object(el último recurso de inferencia), lo cual falla.

Obviamente, si puedes usar una referencia de método, hazlo y funcionará. A veces no se puede utilizar una referencia de método, por ejemplo, si se desea pasar un parámetro adicional, por lo que se debe utilizar una expresión lambda. En ese caso, proporcionarías un tipo de parámetro explícito en la lambda:

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

Es posible que se mejore el compilador para cubrir este caso en una versión futura.

Stuart Marks avatar Aug 07 '2014 03:08 Stuart Marks

Puede solucionar esta limitación utilizando los dos argumentos Comparator.comparingcon Comparator.reverseOrder()como segundo argumento:

users.sort(comparing(User::getName, reverseOrder()));
Misha avatar Aug 07 '2014 21:08 Misha