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:
Это связано с тем, что механизм вывода типов компилятора недостаточно силен, чтобы выполнять два шага одновременно: определить, что для reversed() вызова метода требуется параметр типа LocalDate и, следовательно, для naturalOrder() вызова метода также потребуется параметр того же типа.
Есть способ вызывать методы и явно передавать параметр типа. В простых случаях это не обязательно, потому что это подразумевается, но это можно сделать таким образом:
Но, как показано в принятом на данный момент ответе, все, что помогает компилятору определять тип User для comparing вызова метода без выполнения дополнительных шагов, будет работать, поэтому в этом случае вы также можете явно указать тип лямбда-параметра или использовать ссылку на метод, User::getName которая также включает тип User.
Ответ 4
Статический метод Collections.reverseOrder(Comparator<T>) кажется наиболее элегантным решением из всех предложенных. Только одно предостережение: Comparator.reverseOrder() требует, чтобы T реализовывал comparable и полагался на естественный порядок сортировки.
Collections.reverseOrder(Comparator<T>) не накладывает ограничений на тип T