Я изучал исходный код Java 8 и нашел эту часть кода очень удивительной:
//defined in IntPipeline.java
@Override
public final OptionalInt reduce(IntBinaryOperator op) {
return evaluate(ReduceOps.makeInt(op));
}
@Override
public final OptionalInt max() {
return reduce(Math::max); //this is the gotcha line
}
//defined in Math.java
public static int max(int a, int b) {
return (a >= b) ? a : b;
}
Это Math::max
что-то вроде указателя на метод? Как обычный static
метод конвертируется в IntBinaryOperator
?
TestingLambda$$Lambda$2/8460669
иTestingLambda$$Lambda$3/11043253
были созданы на двух вызовах.Ответы:
Обычно,
reduce
метод вызываетсяMath.max(int, int)
следующим образом:Это требует много синтаксиса для просто вызова
Math.max
. Вот где в игру вступают лямбда-выражения. Начиная с Java 8 разрешено делать то же самое гораздо более коротким способом:Как это работает? Компилятор Java «обнаруживает», что вы хотите реализовать метод, который принимает два
int
s и возвращает одинint
. Это эквивалентно формальным параметрам одного-единственного метода интерфейсаIntBinaryOperator
(параметр метода, которыйreduce
вы хотите вызвать). Таким образом, компилятор сделает все остальное за вас - он просто предполагает, что вы хотите реализоватьIntBinaryOperator
.Но поскольку
Math.max(int, int)
сам выполняет формальные требованияIntBinaryOperator
, он может быть использован напрямую. Поскольку в Java 7 отсутствует синтаксис, позволяющий передавать сам метод в качестве аргумента (вы можете передавать только результаты метода, но не ссылки на метод),::
синтаксис был введен в Java 8 для ссылки на методы:Обратите внимание, что это будет интерпретироваться компилятором, а не JVM во время выполнения! Хотя он генерирует разные байт-коды для всех трех фрагментов кода, они семантически равны, поэтому последние два можно считать короткими (и, вероятно, более эффективными) версиями
IntBinaryOperator
реализации выше!(См. Также перевод лямбда-выражений )
источник
::
называется Ссылка на метод. Это в основном ссылка на один метод. Т.е. это относится к существующему методу по имени.Краткое объяснение :
Ниже приведен пример ссылки на статический метод:
square
может передаваться как ссылки на объекты и запускаться при необходимости. На самом деле, его можно использовать так же легко, как и ссылку на «нормальные» методы объектов, так же какstatic
и их. Например:Function
выше функциональный интерфейс . Чтобы полностью понять::
, важно понимать и функциональные интерфейсы. Проще говоря, функциональный интерфейс - это интерфейс только с одним абстрактным методом.Примеры функциональных интерфейсов включают в себя
Runnable
,Callable
иActionListener
.Function
выше , представляет собой функциональный интерфейс только с одним методом:apply
. Он принимает один аргумент и дает результат.Причина, по которой
::
это здорово, заключается в том, что :Например, вместо написания лямбда-тела
Вы можете просто сделать
Во время выполнения эти два
square
метода ведут себя точно так же, как и другие. Байт-код может совпадать или не совпадать (хотя для вышеупомянутого случая генерируется один и тот же байт-код; скомпилируйте вышеприведенное и проверьте с помощьюjavap -c
).Единственный основной критерий, который необходимо выполнить: метод, который вы предоставляете, должен иметь аналогичную сигнатуру с методом функционального интерфейса, который вы используете в качестве ссылки на объект .
Нижеследующее является незаконным:
square
ожидает аргумент и возвращаетdouble
.get
Метод Поставщиком возвращает значение , но не принимает аргумент. Таким образом, это приводит к ошибке.Ссылка на метод относится к методу функционального интерфейса. (Как уже упоминалось, функциональные интерфейсы могут иметь только один метод каждый).
Еще несколько примеров:
accept
метод в Consumer принимает входные данные, но ничего не возвращает.Выше, не
getRandom
принимает аргументов и возвращаетdouble
. Таким образом, можно использовать любой функциональный интерфейс, который удовлетворяет критериям: не принимать аргументов и возвращатьdouble
.Другой пример:
В случае параметризованных типов :
Ссылки на методы могут иметь разные стили, но по сути все они означают одно и то же и могут просто отображаться как лямбды:
ClassName::methName
)instanceRef::methName
)super::methName
)ClassName::methName
)ClassName::new
)TypeName[]::new
)Для получения дополнительной информации см. Http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html .
источник
Да это правда.
::
Оператор используется для метода реферирования. Таким образом, можно извлечь статические методы из классов, используя его или методы из объектов. Один и тот же оператор может использоваться даже для конструкторов. Все случаи, упомянутые здесь, приведены в примере кода ниже.Официальную документацию от Oracle можно найти здесь .
Вы можете получить лучший обзор изменений JDK 8 в этой статье. В разделе ссылок на метод / конструктор также приведен пример кода:
источник
method(Math::max);
- это вызов, и определение метода будет такимpublic static void method(IntBinaryOperator op){System.out.println(op.applyAsInt(1, 2));}
. Вот как его использовали.Кажется, уже немного поздно, но вот мои два цента. Лямбда - выражение используется для создания анонимных методов. Он не делает ничего, кроме вызова существующего метода, но более понятно ссылаться на метод напрямую по его имени. И ссылка на метод позволяет нам сделать это с помощью оператора метода ссылки
::
.Рассмотрим следующий простой класс, где у каждого сотрудника есть имя и класс.
Предположим, у нас есть список сотрудников, возвращенных каким-либо методом, и мы хотим отсортировать сотрудников по их классам. Мы знаем, что можем использовать анонимный класс как:
где getDummyEmployee () - это некоторый метод как:
Теперь мы знаем, что Comparator - это функциональный интерфейс. Функциональный интерфейс является один ровно один абстрактный метод (хотя он может содержать один или несколько методов по умолчанию или статические). Лямбда-выражение обеспечивает реализацию,
@FunctionalInterface
поэтому функциональный интерфейс может иметь только один абстрактный метод. Мы можем использовать лямбда-выражение как:Кажется, все хорошо, но что, если класс
Employee
также предоставляет аналогичный метод:В этом случае использование самого имени метода будет более понятным. Следовательно, мы можем напрямую ссылаться на метод, используя ссылку на метод как:
Согласно документации существует четыре вида ссылок на методы:
источник
::
это новый оператор, включенный в Java 8, который используется для ссылки на метод существующего класса. Вы можете ссылаться на статические методы и нестатические методы класса.Для ссылки на статические методы используется следующий синтаксис:
Для ссылки на нестатические методы, синтаксис
А также
Единственной предпосылкой для ссылки на метод является то, что метод существует в функциональном интерфейсе, который должен быть совместим со ссылкой на метод.
Ссылки на методы, при оценке, создают экземпляр функционального интерфейса.
Найдено на: http://www.speakingcs.com/2014/08/method-references-in-java-8.html
источник
Это ссылка на метод в Java 8. Документация оракула находится здесь .
Как указано в документации ...
источник
:: Оператор был введен в Java 8 для ссылок на методы. Ссылка на метод - это сокращенный синтаксис для лямбда-выражения, которое выполняет только ОДИН метод. Вот общий синтаксис ссылки на метод:
Мы знаем, что мы можем использовать лямбда-выражения вместо анонимного класса. Но иногда лямбда-выражение на самом деле является просто вызовом некоторого метода, например:
Чтобы сделать код более понятным, вы можете превратить это лямбда-выражение в ссылку на метод:
источник
:: известен как ссылки на метод. Допустим, мы хотим вызвать метод Calculate для класса «Покупка». Тогда мы можем написать это как:
Это также можно рассматривать как краткую форму написания лямбда-выражения, поскольку ссылки на методы преобразуются в лямбда-выражения.
источник
Я нашел этот источник очень интересным.
На самом деле, это лямбда, которая превращается в двойной двоеточие . Двойной двоеточие более читабельно. Мы следуем этим шагам:
ШАГ 1:
ШАГ 2:
ШАГ 3:
источник
Person::getAge()
должно бытьPerson::getAge
.return reduce(Math::max);
это НЕ РАВНО кreturn reduce(max());
Но это значит, что-то вроде этого:
Вы можете просто сохранить 47 нажатий клавиш, если вы напишите так
источник
Поскольку многие ответы здесь хорошо объясняют
::
поведение, дополнительно я хотел бы уточнить, что::
оператору не нужно иметь точно такую же сигнатуру, что и у ссылающегося функционального интерфейса, если он используется для переменных экземпляра . Предположим, нам нужен BinaryOperator, который имеет тип TestObject . Традиционно это реализовано так:Как вы видите в анонимной реализации, он требует два аргумента TestObject и также возвращает объект TestObject. Чтобы выполнить это условие с помощью
::
оператора, мы можем начать со статического метода:а затем позвоните:
Хорошо, это скомпилировано нормально. А что если нам нужен метод экземпляра? Обновим TestObject методом экземпляра:
Теперь мы можем получить доступ к экземпляру, как показано ниже:
Этот код компилируется нормально, но ниже одного нет:
Мое затмение говорит мне: «Невозможно сделать статическую ссылку на нестатический метод testInstance (TestObject, TestObject) из типа TestObject ...»
Достаточно справедливо, это метод экземпляра, но если мы перегружаем,
testInstance
как показано ниже:И позвоните:
Код просто скомпилируется нормально. Потому что он будет вызываться
testInstance
с одним параметром вместо двойного. Итак, что случилось с нашими двумя параметрами? Давайте распечатаем и посмотрим:Который будет выводить:
Итак, JVM достаточно умен, чтобы вызвать param1.testInstance (param2). Можем ли мы использовать
testInstance
из другого ресурса, но не TestObject, то есть:И позвоните:
Он просто не скомпилируется, и компилятор скажет: «Тип TestUtil не определяет testInstance (TestObject, TestObject)» . Поэтому компилятор будет искать статическую ссылку, если она не того же типа. Хорошо, а как насчет полиморфизма? Если мы удалим окончательные модификаторы и добавим наш класс SubTestObject :
И позвоните:
Он также не будет компилироваться, компилятор все равно будет искать статическую ссылку. Но приведенный ниже код прекрасно скомпилируется, поскольку он проходит тест is-a:
источник
В java-8 Streams Reducer в простых работах представляет собой функцию, которая принимает два значения в качестве входных данных и возвращает результат после некоторого вычисления. этот результат подается в следующую итерацию.
в случае функции Math: max метод продолжает возвращать максимум из двух переданных значений, и в итоге у вас на руках наибольшее число.
источник
Во время выполнения они ведут себя совершенно одинаково. Байт-код может / не может быть одинаковым (для вышеупомянутого Incase он генерирует тот же байт-код (соблюдайте выше и проверяйте javaap -c;))
Во время выполнения они ведут себя точно так же .method (math :: max) ;, он генерирует ту же математику (соблюдайте выше и проверьте javap -c;))
источник
В старых версиях Java вместо «::» или lambd вы можете использовать:
Или переходя к методу:
источник
Так что я вижу здесь тонны ответов, которые откровенно слишком сложны, и это преуменьшение.
Ответ довольно прост: :: он называется Method References https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
Так что я не буду копировать-вставлять, по ссылке, вы можете найти всю информацию, если прокрутите вниз до таблицы.
Теперь давайте кратко рассмотрим, что такое ссылки на метод:
A :: B несколько заменяет следующее встроенное лямбда-выражение : (params ...) -> AB (params ...)
Чтобы соотнести это с вашими вопросами, необходимо понять Java-лямбда-выражение. Что не сложно.
Встроенное лямбда-выражение похоже на определенный функциональный интерфейс (который является интерфейсом, который имеет не более и не менее 1 метода) . Давайте кратко рассмотрим, что я имею в виду:
InterfaceX должен быть функциональным интерфейсом. Любой функциональный интерфейс, единственное, что важно для InterfaceX для этого компилятора, это то, что вы определяете формат:
InterfaceX может быть любым из этого:
или это
или более общий:
Давайте возьмем первый представленный случай и встроенное лямбда-выражение, которое мы определили ранее.
До Java 8 вы могли бы определить это так:
Функционально это одно и то же. Разница в том, как компилятор это воспринимает.
Теперь, когда мы рассмотрели встроенное лямбда-выражение, давайте вернемся к методам References (: :). Допустим, у вас есть такой класс:
Так как метод anyFunctions имеет те же типы, что и интерфейсный интерфейс CallMe , мы можем приравнять эти два с помощью метода Reference.
Мы можем написать это так:
и это эквивалентно этому:
Отличная вещь и преимущество ссылок на методы в том, что сначала, пока вы не назначите их переменным, они не будут набирать тип. Таким образом, вы можете передавать их в качестве параметров любому эквивалентному функциональному интерфейсу с одинаковыми типами. Что именно происходит в вашем случае
источник
Предыдущие ответы довольно полны относительно того, что
::
делает ссылка на метод. Подводя итог, он предоставляет способ ссылки на метод (или конструктор) без его выполнения, а при оценке он создает экземпляр функционального интерфейса, который предоставляет целевой контекст типа.Ниже приведены два примера, чтобы найти объект с максимальным значением в
ArrayList
WITH и БЕЗ использования::
ссылки на метод. Пояснения в комментариях ниже.БЕЗ использования
::
С использованием
::
источник
Двойное двоеточие, т.е.
::
оператор введен в Java 8 в качестве ссылки на метод . Ссылка на метод - это форма лямбда-выражения, которая используется для ссылки на существующий метод по его имени.имя_класса :: имяМетода
например: -
stream.forEach(element -> System.out.println(element))
Используя Double Colon
::
stream.forEach(System.out::println(element))
источник