Очень смущен выводом типа компаратора 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())
);


т. е. синтаксические ошибки, потому что он не знает типа

666   4  

4 ответов:

во-первых, все примеры, которые вы говорите, вызывают ошибки, компилируются нормально со ссылочной реализацией (javac от JDK 8.) Они также отлично работают в IntelliJ, поэтому вполне возможно, что ошибки, которые вы видите, являются специфичными для Eclipse.

ваш основной вопрос, кажется, таков: "почему он перестает работать, когда я начинаю цепочку."Причина в том, что в то время как лямбда-выражения и общие вызовы методов Поли выражения (их тип зависит от контекста), когда они появляются в качестве параметров метода, когда они появляются вместо выражений приемника метода, они не являются.

когда вы говорите

Collections.sort(playlist1, comparing(p1 -> p1.getTitle()));

существует достаточно информации о типе для решения как для аргумента типа comparing() и тип аргумента p1. Элемент comparing() вызов получает свой целевой тип из подписи Collections.sort, поэтому comparing() должен вернуть a Comparator<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 способов:

  1. используйте новый синтаксис ссылки на метод Java 8

     Collections.sort(playlist,
                Comparator.comparing(Song::getTitle)
                .thenComparing(Song::getDuration)
                .thenComparing(Song::getArtist)
                );
    
  2. вытащите каждый шаг сравнения в локальную ссылку

      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

  3. принуждение тип, возвращаемый компаратором (обратите внимание, что вам нужен как тип ввода, так и тип ключа сравнения)

    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

    Ничего не найдено.