Вопрос-ответ

Comparator.reversed() does not compile using lambda

Comparator.reversed() не компилируется с использованием lambda

У меня есть список с некоторыми пользовательскими объектами, и я пытаюсь отсортировать список, но работает только с использованием метода reference, при использовании лямбда-выражения компилятор выдает ошибку:

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

Ошибка:

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
Переведено автоматически
Ответ 1

Это слабое место в механизме вывода типов компилятора. Чтобы определить тип u в лямбде, необходимо установить целевой тип для лямбды. Это достигается следующим образом. userList.sort() ожидает аргумент типа Comparator<User>. В первой строке Comparator.comparing() должен быть возвращен Comparator<User>. Это означает, что Comparator.comparing() требуется Function который принимает User аргумент. Таким образом, в лямбде в первой строке, u должно быть типа User и все работает.

Во второй и третьей строках целевой ввод нарушается из-за наличия вызова reversed(). Я не совсем уверен почему; и получатель, и возвращаемый тип reversed() являются Comparator<T> так что кажется, что целевой тип должен быть передан обратно получателю, но это не так. (Как я уже сказал, это слабое место.)

Во второй строке ссылка на метод предоставляет дополнительную информацию о типе, которая заполняет этот пробел. Эта информация отсутствует в третьей строке, поэтому компилятор делает вывод, что u должно быть Object (резервный вариант вывода на крайний случай), который завершается неудачей.

Очевидно, что если вы можете использовать ссылку на метод, сделайте это, и это сработает. Иногда вы не можете использовать ссылку на метод, например, если вы хотите передать дополнительный параметр, поэтому вам приходится использовать лямбда-выражение. В этом случае вам следует указать явный тип параметра в lambda:

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

Возможно, компилятор будет улучшен, чтобы охватить этот случай в будущем выпуске.

Ответ 2

Вы можете обойти это ограничение, используя два аргумента Comparator.comparing with Comparator.reverseOrder() в качестве второго аргумента:

users.sort(comparing(User::getName, reverseOrder()));
Ответ 3

Вопреки принятому и поддержанному ответу, за который была присуждена награда, это на самом деле не имеет никакого отношения к лямбда-выражениям.

Компилируется следующее:

Comparator<LocalDate> dateComparator = naturalOrder();
Comparator<LocalDate> reverseComparator = dateComparator.reversed();

в то время как следующее не компилируется:

Comparator<LocalDate> reverseComparator = naturalOrder().reversed();

Это связано с тем, что механизм вывода типов компилятора недостаточно силен, чтобы выполнять два шага одновременно: определить, что для reversed() вызова метода требуется параметр типа LocalDate и, следовательно, для naturalOrder() вызова метода также потребуется параметр того же типа.

Есть способ вызывать методы и явно передавать параметр типа. В простых случаях это не обязательно, потому что это подразумевается, но это можно сделать таким образом:

Comparator<LocalDate> reverseComparator = Comparator.<LocalDate>naturalOrder().reversed();

В примере, приведенном в вопросе, это стало бы:

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

Но, как показано в принятом на данный момент ответе, все, что помогает компилятору определять тип User для comparing вызова метода без выполнения дополнительных шагов, будет работать, поэтому в этом случае вы также можете явно указать тип лямбда-параметра или использовать ссылку на метод, User::getName которая также включает тип User.

Ответ 4

Статический метод Collections.reverseOrder(Comparator<T>) кажется наиболее элегантным решением из всех предложенных. Только одно предостережение: Comparator.reverseOrder() требует, чтобы T реализовывал comparable и полагался на естественный порядок сортировки.

Collections.reverseOrder(Comparator<T>) не накладывает ограничений на тип T

2023-09-24 22:31 java lambda java-8