Tenho uma lista com alguns objetos de usuário e estou tentando classificar a lista, mas só funciona usando a referência de método, com a expressão lambda que o compilador dá um erro:
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
Erro:
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 Object1 error
Esta é uma fraqueza no mecanismo de inferência de tipo do compilador. Para inferir o tipo de uno lambda, o tipo de destino para o lambda precisa ser estabelecido. Isso é feito da seguinte maneira. userList.sort()está esperando um argumento do tipo Comparator<User>. Na primeira linha, Comparator.comparing()precisa retornar Comparator<User>. Isso implica que Comparator.comparing()precisa de Functionum Userargumento. Assim no lambda da primeira linha, udeve ser do tipo Usere tudo funciona.
Na segunda e terceira linhas, a digitação do destino é interrompida pela presença da chamada para reversed(). Não tenho certeza do porquê; tanto o receptor quanto o tipo de retorno reversed()são, Comparator<T>então parece que o tipo de destino deveria ser propagado de volta para o receptor, mas não é. (Como eu disse, é uma fraqueza.)
Na segunda linha, a referência do método fornece informações de tipo adicionais que preenchem essa lacuna. Essas informações estão ausentes na terceira linha, portanto, o compilador infere user Object(o fallback de inferência de último recurso), o que falha.
Obviamente, se você pode usar uma referência de método, faça isso e funcionará. Às vezes, você não pode usar uma referência de método, por exemplo, se quiser passar um parâmetro adicional, então você tem que usar uma expressão lambda. Nesse caso, você forneceria um tipo de parâmetro explícito no lambda:
Lambdas são divididos em digitados implicitamente (sem tipos de manifesto para parâmetros) e digitados explicitamente ; as referências de método são divididas em exatas (sem sobrecargas) e inexatas . Quando uma chamada de método genérico em uma posição de receptor tem argumentos lambda, e os parâmetros de tipo não podem ser totalmente inferidos dos outros argumentos, você precisa fornecer um lambda explícito, um método ref exato, um cast de tipo de destino ou testemunhas de tipo explícito para a chamada de método genérico para fornecer as informações de tipo adicionais necessárias para prosseguir.
Brian Goetz
1
@StuartMarks, você "não tem certeza porque" o compilador está agindo assim. Mas o que diz a especificação da linguagem ? Deve haver informações suficientes para determinar os tipos genéricos, de acordo com a especificação do idioma? Nesse caso, este é um bug do compilador e deve ser arquivado e tratado de acordo. Caso contrário, é uma área na qual a linguagem Java deve ser aprimorada. Qual é?
Garret Wilson
8
Acho que podemos tratar os comentários de Brian como definitivos, já que ele escreveu a especificação em questão :-)
minimalis
1
Infelizmente, nada disso explica por que ele está funcionando sem reverso enquanto não está funcionando com reverso.
Chris311
90
Você pode contornar essa limitação usando os dois argumentos Comparator.comparingcom Comparator.reverseOrder()o segundo argumento:
Você pode contornar essa limitação usando os dois argumentos
Comparator.comparing
comComparator.reverseOrder()
o segundo argumento:fonte
users.sort(reverseOrder(comparing(User::getName)));
,.reverseOrder(Comparator<T>)
método acima é injava.util.Collections
, não inComparator
.