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

111

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

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
Андрей
источник

Ответы:

145

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

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

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

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

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

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

Стюарт Маркс
источник
28
Лямбда-выражения делятся на неявно типизированные (без манифестных типов для параметров) и явно типизированные ; ссылки на методы делятся на точные (без перегрузок) и неточные . Когда общий вызов метода в позиции получателя имеет лямбда-аргументы, а параметры типа не могут быть полностью выведены из других аргументов, вам необходимо предоставить либо явную лямбда, либо точную ссылку на метод, приведение целевого типа, либо явные свидетели типа для вызов универсального метода для предоставления дополнительной информации о типе, необходимой для продолжения.
Брайан Гетц
1
@StuartMarks, вы «не совсем уверены, почему» компилятор так себя ведет. Но что говорится в спецификации языка ? Должно ли быть достаточно информации для определения общих типов в соответствии со спецификацией языка? Если это так, то это ошибка компилятора, которую следует зарегистрировать и устранить соответствующим образом. В противном случае это область, в которой язык Java должен быть улучшен. Что это?
Гаррет Уилсон
8
Думаю, мы можем рассматривать комментарии Брайана как окончательные, так как он написал рассматриваемую спецификацию :-)
minimalis
1
К сожалению, ничто из этого не объясняет, почему он работает без реверса, в то время как он не работает с реверсом.
Chris311
90

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

users.sort(comparing(User::getName, reverseOrder()));
Миша
источник
4
Ницца. Мне это нравится больше, чем использование явно типизированной лямбды. Или, еще лучше, users.sort(reverseOrder(comparing(User::getName)));.
rolve
10
Обратите внимание, что приведенный reverseOrder(Comparator<T>)выше метод находится java.util.Collectionsв Comparator.
rolve