Comparator.reversed () не компілюється за допомогою лямбда


111

У мене є список з деякими об'єктами користувача, і я намагаюся сортувати список, але працює лише з використанням методу посилання, з виразом лямбда компілятор видає помилку:

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
Лямбди поділяються на неявно типізовані (відсутні види маніфестів для параметрів) та явно набрані ; Довідки методів поділяються на точні (без перевантажень) та неточні . Коли виклик загального методу в позиції приймача має аргументи лямбда, і параметри типу не можуть бути повністю виведені з інших аргументів, вам потрібно надати або явну лямбда, точний метод ref, тип цільового типу, або явні свідки типу для загальний виклик методу, щоб надати додаткову інформацію про тип, необхідну для продовження.
Брайан Гетц

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.