Функциональная композиция — это техника для объединения нескольких функций в одну, которая использует объединенные функции внутри себя. Вы можете объединить отдельные функции (обычно одно или несколько лямбда-выражений) в одну самостоятельно, но Java также поставляется со встроенной поддержкой функционального состава, чтобы упростить вам задачу.
Пример
Вот одна функция, состоящая из двух других:
Predicate startsWithA =(text) -> text.startsWith("A");
Predicate endsWithX =(text) -> text.endsWith("x");
Predicate startsWithAAndEndsWithX =
(text) -> startsWithA.test(text) endsWithX.test(text);
String input = "A hardworking person must relax";
boolean result = startsWithAAndEndsWithX.test(input);
System.out.println(result);
Этот пример сначала создает две реализации Predicate в форме двух лямбда-выражений. Первый возвращает true, если строка, которую вы передаете ему в качестве параметра, начинается с заглавной буквы a(A). Второй возвращает true, если переданная ему строка заканчивается строчной буквой x.
Обратите внимание, что интерфейс Predicate содержит единственный не реализованный метод с именем test(), который возвращает логическое значение. Именно этот метод реализуют лямбда-выражения.
После создания двух основных функций создается третий Predicate, который вызывает методы test() двух первых функций. Эта функция возвращает true, если обе основные возвращают true, и false в противном случае.
Наконец, этот пример вызывает составленную функцию и выводит результат. Поскольку текст начинается с прописной буквы a(A) и заканчивается строчной буквой x, составная функция возвращает true, когда вызывается со строкой «A hardworking person must relax».
Поддержка
В примере из предыдущего раздела показано, как составить новую функцию из двух других. Некоторые из функциональных интерфейсов уже имеют встроенную поддержку функциональной композиции. Она имеет форму стандартных и статических методов в функциональных интерфейсах.
Predicate
Интерфейс Predicate (java.util.function.Predicate) содержит несколько методов, которые помогут вам создавать новые экземпляры Predicate из других экземпляров.
and()
Является методом по умолчанию и используется для объединения двух других функций Predicate:
Predicate startsWithA =(text) -> text.startsWith("A");
Predicate endsWithX =(text) -> text.endsWith("x");
Predicate composed = startsWithA.and(endsWithX);
String input = "A hardworking person must relax";
boolean result = composed.test(input);
System.out.println(result);
В этом примере составления Predicate создается новый из двух других экземпляров с использованием метода and() одного из базовых экземпляров.
Составленный Predicate вернет истину из своего метода test(), если оба экземпляра, из которых он был составлен, также вернут истину. Другими словами, если оба возвращают значение true.
or()
or() используется для объединения экземпляра Predicate с другим, чтобы создать третий экземпляр. Составленный вернет true, если любой из экземпляров, из которых он состоит, возвращает истину, когда их методы test() вызываются с тем же входным параметром, что и составленный:
Predicate startsWithA =(text) -> text.startsWith("A");
Predicate endsWithX =(text) -> text.endsWith("x");
Predicate composed = startsWithA.or(endsWithX);
String input = "A hardworking person must relax sometimes";
boolean result = composed.test(input);
System.out.println(result);
- Сначала создается два основных экземпляра Predicate.
- Создается третий предикат, составленный из первых двух, путем вызова метода or() в первом и передачи второго в качестве параметра методу or().
Результат выполнения приведенного выше примера будет истинным, потому что первый из двух экземпляров Predicate, используемых в составленном, вернет true при вызове со строкой «A hardworking person must relax sometimes».
Композиция функций
Интерфейс Function (java.util.function.Function) также содержит несколько методов, которые можно использовать для создания новых экземпляров Function из существующих.
compose()
Метод compose() создает новый экземпляр Function из экземпляра Function, к которому он вызван, и экземпляр Function, передаваемый в качестве параметра методу.
Функция, возвращаемая compose(), сначала вызовет функцию, переданную в качестве параметра compose(), а затем вызовет функцию, для которой был вызван метод. Это легче понять на примере:
Function multiply =(value) -> value * 2; Function add =(value) -> value + 3; Function addThenMultiply = multiply.compose(add); Integer result1 = addThenMultiply.apply(3); System.out.println(result1);
При вызове со значением 3 составная функция сначала вызывает функцию сложения, а затем функцию умножения. Итоговый расчет будет (3 + 3) * 2, а результат будет 12.
andThen()
Работает противоположно методу compose(). Функция, составленная с помощью andThen(), сначала вызывает функцию, для которой была вызвана andThen(), а затем вызывает функцию, переданную в качестве параметра методу andThen():
Function multiply =(value) -> value * 2; Function add =(value) -> value + 3; Function multiplyThenAdd = multiply.andThen(add); Integer result2 = multiplyThenAdd.apply(3); System.out.println(result2);
Этот пример сначала создает функцию умножения и функцию добавления. Затем вызывается метод andThen() для функции умножения для создания новой функции, передавая функцию добавления в качестве параметра функции andThen().
Вызов функции, составленной методом andThen() со значением 3, приведет к следующему вычислению 3 * 2 + 3, и результат будет 9.
Примечание: как уже упоминалось в начале, andThen() работает напротив compose(). Следовательно, вызов a.andThen(b) фактически совпадает с вызовом b.compose(a).
