Очень смущен выводом типа компаратора Java 8
я смотрел на разницу между Collections.sort и list.sort, в частности, в отношении использования Comparator статические методы и требуются ли типы param в лямбда-выражениях. Прежде чем мы начнем, я знаю, что могу использовать ссылки на методы, например Song::getTitle чтобы преодолеть мои проблемы, но мой запрос здесь не столько то, что я хочу исправить, но то, что я хочу получить ответ, т. е. почему компилятор Java обрабатывает его таким образом.
это моя находка. Предположим, что мы есть ArrayList типа Song, С некоторыми добавленными песнями, есть 3 стандартных метода get:
ArrayList<Song> playlist1 = new ArrayList<Song>();
//add some new Song objects
playlist.addSong( new Song("Only Girl (In The World)", 235, "Rhianna") );
playlist.addSong( new Song("Thinking of Me", 206, "Olly Murs") );
playlist.addSong( new Song("Raise Your Glass", 202,"P!nk") );
вот вызов обоих типов метода сортировки, который работает, без проблем:
Collections.sort(playlist1,
Comparator.comparing(p1 -> p1.getTitle()));
playlist1.sort(
Comparator.comparing(p1 -> p1.getTitle()));
как только я начну цеплять thenComparing происходит следующее:
Collections.sort(playlist1,
Comparator.comparing(p1 -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
playlist1.sort(
Comparator.comparing(p1 -> p1.getTitle())
.thenComparing(p1 -> p1.getDuration())
.thenComparing(p1 -> p1.getArtist())
);
т. е. синтаксические ошибки, потому что он не знает типа
4 ответов:
во-первых, все примеры, которые вы говорите, вызывают ошибки, компилируются нормально со ссылочной реализацией (javac от JDK 8.) Они также отлично работают в IntelliJ, поэтому вполне возможно, что ошибки, которые вы видите, являются специфичными для Eclipse.
ваш основной вопрос, кажется, таков: "почему он перестает работать, когда я начинаю цепочку."Причина в том, что в то время как лямбда-выражения и общие вызовы методов Поли выражения (их тип зависит от контекста), когда они появляются в качестве параметров метода, когда они появляются вместо выражений приемника метода, они не являются.
когда вы говорите
Collections.sort(playlist1, comparing(p1 -> p1.getTitle()));существует достаточно информации о типе для решения как для аргумента типа
comparing()и тип аргументаp1. Элементcomparing()вызов получает свой целевой тип из подписиCollections.sort, поэтомуcomparing()должен вернуть aComparator<Song>, и поэтомуp1должно бытьSong.но когда вы начинаете цепочка:
Collections.sort(playlist1, comparing(p1 -> p1.getTitle()) .thenComparing(p1 -> p1.getDuration()) .thenComparing(p1 -> p1.getArtist()));теперь у нас есть проблема. Мы знаем, что составное выражение
comparing(...).thenComparing(...)имеет целевой типComparator<Song>, а потому что выражение приемника для цепочки,comparing(p -> p.getTitle()), это общий вызов метода, и мы не можем вывести его параметры типа из других аргументов, нам не повезло. Поскольку мы не знаем тип этого выражения, мы не знаем, что оно имеетthenComparingметод, etc.есть несколько способов, чтобы исправить это, все необходимо ввести больше информации о типе, чтобы исходный объект в цепочке мог быть правильно набран. Вот они, в грубом порядке убывающей желательности и возрастающей навязчивости:
- используйте точную ссылку на метод (без перегрузок), например
Song::getTitle. Затем это дает достаточно информации о типе, чтобы вывести переменные типа дляcomparing()вызовите, и поэтому дайте ему тип, и поэтому продолжайте вниз по цепочке.- использовать явную лямбду (как вы сделали в своем примере).
- укажите тип свидетеля для
comparing()звоните:Comparator.<Song, String>comparing(...).- предоставьте явный целевой тип с приведением, приведя выражение приемника к
Comparator<Song>.
проблема заключается в выводе типа. Без добавления
(Song s)к первому сравнению,comparator.comparingне знает тип входных данных, поэтому по умолчанию используется Object.вы можете решить эту проблему 1 из 3 способов:
используйте новый синтаксис ссылки на метод Java 8
Collections.sort(playlist, Comparator.comparing(Song::getTitle) .thenComparing(Song::getDuration) .thenComparing(Song::getArtist) );вытащите каждый шаг сравнения в локальную ссылку
Comparator<Song> byName = (s1, s2) -> s1.getArtist().compareTo(s2.getArtist()); Comparator<Song> byDuration = (s1, s2) -> Integer.compare(s1.getDuration(), s2.getDuration()); Collections.sort(playlist, byName .thenComparing(byDuration) );EDIT
принуждение тип, возвращаемый компаратором (обратите внимание, что вам нужен как тип ввода, так и тип ключа сравнения)
sort( Comparator.<Song, String>comparing((s) -> s.getTitle()) .thenComparing(p1 -> p1.getDuration()) .thenComparing(p1 -> p1.getArtist()) );Я думаю, что "последний"
thenComparingсинтаксическая ошибка вводит вас в заблуждение. На самом деле это проблема типа со всей цепочкой, это просто компилятор, отмечающий только конец цепочки как синтаксическую ошибку, потому что именно тогда окончательный тип возврата не соответствует, Я думаю.Я не уверен, почему
Listделает лучшую работу вывода, чемCollectionтак как это должен делать тот же тип захвата, но, по-видимому, нет.
playlist1.sort(...)создает границу песни для переменной типа E, от объявления playlist1, которая "рябит" к компаратору.на
Collections.sort(...), такой привязки нет, и вывод из типа первого компаратора недостаточно для компилятора, чтобы вывести остальное.Я думаю, что вы получите "правильное" поведение от
Collections.<Song>sort(...), но у вас нет установки java 8, чтобы проверить ее для вас.
другой способ справиться с этой ошибкой времени компиляции:
приведите свою первую переменную функции сравнения явно, а затем хорошо идти. У меня есть список орг.бсон.Документы объекта. Пожалуйста, посмотрите на пример кода
Comparator<Document> comparator = Comparator.comparing((Document hist) -> (String) hist.get("orderLineStatus"), reverseOrder()) .thenComparing(hist -> (Date) hist.get("promisedShipDate")) .thenComparing(hist -> (Date) hist.get("lastShipDate")); list = list.stream().sorted(comparator).collect(Collectors.toList());
Comments