Это требует большого синтаксиса для простого вызова Math.max. Вот тут-то и вступают в игру лямбда-выражения. Начиная с Java 8, разрешено делать то же самое гораздо более коротким способом:
reduce((int left, int right) -> Math.max(left, right));
Как это работает? Компилятор Java "обнаруживает", что вы хотите реализовать метод, который принимает два ints и возвращает один int. Это эквивалентно формальным параметрам одного-единственного метода интерфейса IntBinaryOperator (параметр метода, который reduce вы хотите вызвать). Итак, компилятор сделает все остальное за вас - он просто предполагает , что вы хотите реализовать IntBinaryOperator.
Но поскольку Math.max(int, int) сам по себе удовлетворяет формальным требованиям IntBinaryOperator, его можно использовать напрямую. Поскольку Java 7 не имеет синтаксиса, который позволяет передавать сам метод в качестве аргумента (вы можете передавать только результаты метода, но никогда ссылки на методы), :: синтаксис был введен в Java 8 для ссылки на методы:
reduce(Math::max);
Обратите внимание, что это будет интерпретироваться компилятором, а не JVM во время выполнения! Хотя он создает разные байт-коды для всех трех фрагментов кода, они семантически равны, поэтому последние два можно рассматривать как короткие (и, вероятно, более эффективные) версии IntBinaryOperator приведенной выше реализации!
square может передаваться точно так же, как ссылки на объекты, и запускаться при необходимости. Фактически, его можно так же легко использовать как ссылку на "обычные" методы объектов, как и static ones . Например:
Function выше приведен функциональный интерфейс. Для полного понимания :: важно понимать и функциональные интерфейсы. Очевидно, что функциональный интерфейс - это интерфейс только с одним абстрактным методом.
Примеры функциональных интерфейсов включают Runnable, Callable и ActionListener.
Function выше приведен функциональный интерфейс только с одним методом: apply. Он принимает один аргумент и выдает результат.
Причина, по которой ::они потрясающие, заключается в том, что:
Ссылки на методы - это выражения, которые обрабатываются так же, как и лямбда-выражения (...), но вместо предоставления тела метода они ссылаются на существующий метод по имени.
Например, вместо записи тела лямбда
Function<Double, Double> square = (Double x) -> x * x;
Вы можете просто сделать
Function<Double, Double> square = Hey::square;
Во время выполнения эти два square метода ведут себя точно так же, как друг друга. Байт-код может совпадать, а может и не совпадать (хотя в приведенном выше случае генерируется один и тот же байт-код; скомпилируйте приведенный выше и проверьте с помощью javap -c).
Единственный важный критерий, которому необходимо удовлетворять: предоставляемый вами метод должен иметь сигнатуру, аналогичную сигнатуре метода функционального интерфейса, который вы используете в качестве ссылки на объект.
Приведенное ниже является незаконным:
Supplier<Boolean> p = Hey::square; // illegal
square ожидает аргумент и возвращает double. get Метод в поставщике возвращает значение, но не принимает аргумент. Таким образом, это приводит к ошибке.
Ссылка на метод относится к методу функционального интерфейса. (Как упоминалось, функциональные интерфейсы могут иметь только один метод каждый.)
Еще несколько примеров: accept метод в consumer принимает входные данные, но ничего не возвращает.
Callable<Double> call = hey::getRandom; Supplier<Double> call2 = hey::getRandom; DoubleSuppliersup= hey::getRandom; // Supplier is functional interface that takes no argument and gives a result
Выше, getRandom не принимает никаких аргументов и возвращает double. Таким образом, может использоваться любой функциональный интерфейс, который удовлетворяет критериям: не принимать аргументов и возвращать double.
Другой пример:
Set<String> set = newHashSet<>(); set.addAll(Arrays.asList("leo","bale","hanks")); Predicate<String> pred = set::contains; booleanexists= pred.test("leo");
В случае параметризованных типов:
classParam<T> { T elem; public T get() { return elem; }
publicvoidset(T elem) { this.elem = elem; }
publicstatic <E> E returnSame(E elem) { return elem; } }
Supplier<Param<Integer>> obj = Param<Integer>::new; Param<Integer> param = obj.get(); Consumer<Integer> c = param::set; Supplier<Integer> s = param::get;
Да, это правда. Оператор :: используется для ссылки на методы. Таким образом, с его помощью можно извлекать статические методы из классов или методы из объектов. Один и тот же оператор может использоваться даже для конструкторов. Все упомянутые здесь случаи проиллюстрированы в приведенном ниже примере кода.
Официальную документацию Oracle можно найти здесь.
Вы можете получить более подробный обзор изменений в JDK 8 в этой статье. В разделе "Ссылки на метод / конструктор" также приведен пример кода:
interfaceConstructorReference { T constructor(); }
Для создания анонимных методов используется лямбда-выражение. Оно ничего не делает, кроме вызова существующего метода, но понятнее ссылаться на метод непосредственно по его имени. И ссылка на метод позволяет нам сделать это с помощью оператора ссылки на метод :: .
Рассмотрим следующий простой класс, в котором у каждого сотрудника есть имя и ранг.
Предположим, у нас есть список сотрудников, возвращаемый каким-либо методом, и мы хотим отсортировать сотрудников по их классу. Мы знаем, что можем использовать анонимный класс как:
Теперь мы знаем, что Comparator - это функциональный интерфейс. Функциональный интерфейс - это интерфейс с ровно одним абстрактным методом (хотя он может содержать один или несколько стандартных или статических методов). Лямбда-выражение обеспечивает реализацию @FunctionalInterface поэтому функциональный интерфейс может иметь только один абстрактный метод. Мы можем использовать лямбда-выражение как:
В этом случае использование самого имени метода будет более понятным. Следовательно, мы можем напрямую ссылаться на метод, используя ссылку на метод как: EmployeeList.sort(employee::compareByGrade); // Ссылка на метод
Согласно документации, существует четыре вида ссылок на методы:
+----+-------------------------------------------------------+--------------------------------------+ | | Kind | Example | +----+-------------------------------------------------------+--------------------------------------+ | 1 | Reference to a static method | ContainingClass::staticMethodName | +----+-------------------------------------------------------+--------------------------------------+ | 2 |Reference to an instance method of a particular object | containingObject::instanceMethodName | +----+-------------------------------------------------------+--------------------------------------+ | 3 | Reference to an instance method of an arbitrary object| ContainingType::methodName | | | of a particular type | | +----+-------------------------------------------------------+--------------------------------------+ | 4 |Reference to a constructor | ClassName::new | +------------------------------------------------------------+--------------------------------------+