Java 8 stream's. min () and.max (): почему это компилируется?



Примечание: этот вопрос возникает из мертвой ссылки, которая была предыдущим вопросом SO, но здесь идет...



посмотреть этот код (примечание: Я знаю, что этот код не будет "работать" и Integer::compare должен использоваться -- я только что извлек его из связанного вопроса):



final ArrayList <Integer> list 
= IntStream.rangeClosed(1, 20).boxed().collect(Collectors.toList());

System.out.println(list.stream().max(Integer::max).get());
System.out.println(list.stream().min(Integer::min).get());


в соответствии с документацией о .min() и .max(), аргумент обоих должен быть Comparator. Однако здесь Ссылки на методы относятся к статическим методам из Integer класса.



Итак, почему это вообще компилируется?

606   5  

5 ответов:

позвольте мне объяснить, что здесь происходит, потому что это не очевидно!

первый, Stream.max() принимает экземпляр Comparator чтобы элементы в потоке можно было сравнить друг с другом, чтобы найти минимум или максимум, в некотором оптимальном порядке, о котором вам не нужно слишком беспокоиться.

так что вопрос, конечно, почему Integer::max принято? Ведь это не компаратор!

ответ таким образом, новая функциональность лямбда работает в Java 8. Он опирается на концепцию, которая неофициально известна как интерфейсы "единого абстрактного метода" или интерфейсы "SAM". Идея заключается в том, что любой интерфейс с одним абстрактным методом может быть автоматически реализован любой лямбда - или ссылкой на метод, чья сигнатура метода соответствует одному методу на интерфейсе. Так что изучаем Comparator интерфейс (простой вариант):

public Comparator<T> {
    int compare(T o1, T o2);
}

если метод в поисках Comparator<Integer>, то он по существу ищет эту подпись:

int xxx(Integer o1, Integer o2);

я использую "xxx"потому что имя метода не используется для соответствующих целей.

таким образом,Integer.min(int a, int b) и Integer.max(int a, int b) достаточно близко, что автобоксинг позволит этому появиться как Comparator<Integer> в контексте метода.

Comparator это функционального интерфейса и Integer::max соответствует этому интерфейсу (после автобоксинга/распаковки принимается во внимание). Это займет два int значения и возвращает int - как и следовало ожидать Comparator<Integer> to (опять же, прищурившись, чтобы игнорировать разницу Integer/int).

однако, я бы не ожидал, что он будет делать правильные вещи, учитывая, что Integer.max не соответствует семантика на Comparator.compare. И действительно, это не так работать вообще. Например, внесите одно небольшое изменение:

for (int i = 1; i <= 20; i++)
    list.add(-i);

... а теперь max значение -20 и min значение равно -1.

вместо этого, оба вызова должны использовать Integer::compare:

System.out.println(list.stream().max(Integer::compare).get());
System.out.println(list.stream().min(Integer::compare).get());

это работает, потому что Integer::min разрешает реализацию Comparator<Integer> интерфейс.

ссылка на метод Integer::min разрешает Integer.min(int a, int b), решил IntBinaryOperator, и предположительно автобоксинг происходит где-то делает его BinaryOperator<Integer>.

и min() респ max() методы Stream<Integer> задать вопрос Comparator<Integer> интерфейс для реализации.
Теперь это разрешается в один метод Integer compareTo(Integer o1, Integer o2). Который имеет тип BinaryOperator<Integer>.

и таким образом магия произошла, поскольку оба метода являются BinaryOperator<Integer>.

помимо информации, предоставленной Дэвидом М. Ллойдом, можно добавить, что механизм, который позволяет это, называется цель ввода.

идея заключается в том, что тип, который компилятор присваивает лямбда-выражениям или ссылкам на метод, зависит не только от самого выражения, но и от того, где оно используется.

целью выражения является переменная, которой присваивается его результат, или параметр, которому присваивается его результат пройденный.

лямбда-выражения и ссылки на методы присваиваются типу, который соответствует типу их цели, если такой тип может быть найден.

посмотреть раздел определение типа в Java для получения дополнительной информации.

У меня была ошибка с массивом, получающим max и min, поэтому мое решение было:

int max = Arrays.stream(arrayWithInts).max().getAsInt();
int min = Arrays.stream(arrayWithInts).min().getAsInt();

Comments

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