Comparator.reversed() no se compila usando lambda
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
Esta es una debilidad en el mecanismo de inferencia de tipos del compilador. Para inferir el tipo de u
lambda, 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 Function
un User
argumento. Así, en la lambda de la primera línea, u
debe ser de tipo User
y 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 u
que 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.
Puede solucionar esta limitación utilizando los dos argumentos Comparator.comparing
con Comparator.reverseOrder()
como segundo argumento:
users.sort(comparing(User::getName, reverseOrder()));