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 класса.
Итак, почему это вообще компилируется?
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