Примечание: этот вопрос возник из мертвой ссылки, которая была предыдущим вопросом 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());
Согласно Javadoc .min()
и .max()
, аргумент обоих должен быть Comparator
. И все же здесь ссылки на методы относятся к статическим методам Integer
класса.
Итак, почему это компилируется вообще?
Integer::compare
вместоInteger::max
иInteger::min
.Integer
не являются методамиComparator
.Ответы:
Позвольте мне объяснить, что здесь происходит, потому что это не очевидно!
Во-первых,
Stream.max()
принимает экземплярComparator
так, чтобы элементы в потоке могли сравниваться друг с другом, чтобы найти минимум или максимум, в некотором оптимальном порядке, о котором вам не нужно слишком беспокоиться.Так что вопрос, конечно, почему
Integer::max
принимается? Ведь это не компаратор!Ответ заключается в том, как функционирует новая лямбда-функция в Java 8. Она опирается на концепцию, неофициально известную как интерфейсы «одного абстрактного метода» или интерфейсы «SAM». Идея состоит в том, что любой интерфейс с одним абстрактным методом может быть автоматически реализован любой лямбда - или ссылкой на метод - чья сигнатура метода совпадает с одним методом в интерфейсе. Итак, изучим
Comparator
интерфейс (простая версия):Если метод ищет
Comparator<Integer>
, то он по сути ищет эту сигнатуру:Я использую «ххх», потому что имя метода не используется для сопоставления .
Следовательно, оба
Integer.min(int a, int b)
иInteger.max(int a, int b)
достаточно близки, чтобы автобокс позволял этому появлятьсяComparator<Integer>
в контексте метода.источник
list.stream().mapToInt(i -> i).max().get()
..getAsInt()
вместо,get()
хотя, как вы имеете дело сOptionalInt
.max()
функции!Comparator
документацию, мы видим, что он украшен аннотацией@FunctionalInterface
. Этот декоратор - это магия, которая позволяетInteger::max
иInteger::min
превращается вComparator
.@FunctionalInterface
в основном только для целей документирования, так как компилятор может с радостью сделать это с любым интерфейсом с помощью одного единственного абстрактного метода.Comparator
является функциональным интерфейсом иInteger::max
соответствует этому интерфейсу (после того, как принимается во внимание автобокс / распаковка). Он принимает дваint
значения и возвращаетint
- так же, как вы ожидаете aComparator<Integer>
(опять же, щурясь, чтобы игнорировать разность Integer / int).Тем не менее, я бы не ожидал , что это сделать правильно, учитывая , что
Integer.max
не соответствует с семантикой оComparator.compare
. И действительно, это не очень работает в целом. Например, сделайте одно небольшое изменение:... и теперь это
max
значение -20, аmin
значение -1.Вместо этого оба вызова должны использовать
Integer::compare
:источник
Comparator<Integer>
было быint compare(Integer, Integer)
... это не ошеломляет, что Java позволяет ссылаться на методint max(int, int)
для преобразования в это ...Integer::max
? С его точки зрения, вы передали функцию, которая соответствует его спецификации, вот и все, на что она действительно способна.Comparator.compare
. Она должна возвращатьenum
из{LessThan, GreaterThan, Equal}
, а неint
. Таким образом, функциональный интерфейс на самом деле не будет совпадать, и вы получите ошибку компиляции. IOW: сигнатура типаComparator.compare
неадекватно отражает семантику того, что означает сравнение двух объектов, и, следовательно, другие интерфейсы, которые абсолютно не имеют ничего общего со случайным сравнением объектов, имеют одинаковую сигнатуру типа.Это работает, потому что
Integer::min
разрешает реализациюComparator<Integer>
интерфейса.Ссылка на метод
Integer::min
разрешаетInteger.min(int a, int b)
, разрешаетIntBinaryOperator
и, по-видимому, автобокс происходит где-то, делая его aBinaryOperator<Integer>
.И
min()
соответствующиеmax()
методыStream<Integer>
требуют, чтобыComparator<Integer>
интерфейс был реализован.Теперь это разрешает единственный метод
Integer compareTo(Integer o1, Integer o2)
. Который имеет типBinaryOperator<Integer>
.И, таким образом, волшебство произошло, поскольку оба метода являются
BinaryOperator<Integer>
.источник
Integer::min
реализуетComparable
. Это не тот тип, который может реализовать что угодно. Но это оценивается в объект, который реализуетComparable
.Comparator<Integer>
является интерфейсом с одним абстрактным методом (он же «функциональный») иInteger::min
выполняет свой контракт, поэтому лямбда-выражение можно интерпретировать следующим образом. Я не знаю, как вы видите, как здесь вступает в игру BinaryOperator (или IntBinaryOperator) - между этим и Comparator нет никакой подтиповой связи.Помимо информации, предоставленной Дэвидом М. Ллойдом, можно добавить, что механизм, который позволяет это, называется целевой типизацией .
Идея состоит в том, что тип, который компилятор присваивает лямбда-выражениям или ссылкам на метод, зависит не только от самого выражения, но и от того, где оно используется.
Целью выражения является переменная, которой присвоен его результат, или параметр, которому передается его результат.
Лямбда-выражениям и ссылкам на методы присваивается тип, который соответствует типу их цели, если такой тип может быть найден.
Посмотрите раздел Вывода типов в Учебном руководстве по Java для получения дополнительной информации.
источник
У меня была ошибка с массивом, получающим максимальное и минимальное значения, поэтому мое решение было:
источник