1. Обзор
В этом уроке мы дадим краткий обзор Функциональной библиотеки Java вместе с несколькими примерами.
2. Функциональная Библиотека Java
Функциональная библиотека Java-это библиотека с открытым исходным кодом, предназначенная для облегчения функционального программирования на Java. Библиотека предоставляет множество базовых и продвинутых абстракций программирования, обычно используемых в функциональном программировании .
Большая часть функциональности библиотеки вращается вокруг интерфейса F . Этот F интерфейс моделирует функцию, которая принимает входные данные типа A и возвращает выходные данные типа B . Все это построено поверх собственной системы типов Java.
3. Зависимости Maven
Во-первых, нам нужно добавить необходимые зависимости в ваш pom.xml файл:
org.functionaljava functionaljava 4.8.1 org.functionaljava functionaljava-java8 4.8.1 org.functionaljava functionaljava-quickcheck 4.8.1 org.functionaljava functionaljava-java-core 4.8.1
4. Определение функции
Давайте начнем с создания функции, которую мы сможем использовать в наших примерах позже.
Без функциональной Java базовый метод умножения выглядел бы примерно так:
public static final Integer timesTwoRegular(Integer i) { return i * 2; }
Используя функциональную библиотеку Java, мы можем определить эту функциональность немного более элегантно:
public static final FtimesTwo = i -> i * 2;
Выше мы видим пример F интерфейс, который принимает Целое число в качестве входных данных и возвращает, что Целое число умножьте два раза на его выход.
Вот еще один пример базовой функции, которая принимает Целое число в качестве входных данных, но в этом случае возвращает Логическое значение , чтобы указать, был ли ввод четным или нечетным:
public static final FisEven = i -> i % 2 == 0;
5. Применение функции
Теперь, когда у нас есть наши функции, давайте применим их к набору данных.
Функциональная библиотека Java предоставляет обычный набор типов для управления данными, такими как списки, наборы, массивы и карты. Главное, что нужно понять, – это то, что эти типы данных неизменяемы.
Кроме того, библиотека предоставляет удобные функции для преобразования в стандартные классы коллекций Java и из них при необходимости.
В приведенном ниже примере мы определим список целых чисел и применим к нему нашу функцию timesTwo . Мы также вызовем map , используя встроенное определение той же функции. Конечно, мы ожидаем, что результаты будут такими же:
public void multiplyNumbers_givenIntList_returnTrue() { ListfList = List.list(1, 2, 3, 4); List fList1 = fList.map(timesTwo); List fList2 = fList.map(i -> i * 2); assertTrue(fList1.equals(fList2)); }
Как мы видим, map возвращает список одинакового размера, где значение каждого элемента является значением входного списка с примененной функцией. Сам список входных данных не меняется.
Вот аналогичный пример с использованием нашей функции isEven :
public void calculateEvenNumbers_givenIntList_returnTrue() { ListfList = List.list(3, 4, 5, 6); List evenList = fList.map(isEven); List evenListTrueResult = List.list(false, true, false, true); assertTrue(evenList.equals(evenListTrueResult)); }
Поскольку метод map возвращает список, мы можем применить другую функцию к его выходу. Порядок, в котором мы вызываем наши функции map , изменяет наш результирующий вывод:
public void applyMultipleFunctions_givenIntList_returnFalse() { ListfList = List.list(1, 2, 3, 4); List fList1 = fList.map(timesTwo).map(plusOne); List fList2 = fList.map(plusOne).map(timesTwo); assertFalse(fList1.equals(fList2)); }
Результатом приведенных выше списков будет:
List(3,5,7,9) List(4,6,8,10)
6. Фильтрация с помощью функции
Другой часто используемой операцией в функциональном программировании является ввод и фильтрация данных на основе некоторых критериев . И, как вы, вероятно, уже догадались, эти критерии фильтрации представлены в виде функции. Эта функция должна будет возвращать логическое значение, чтобы указать, нужно ли включать данные в выходные данные.
Теперь давайте используем нашу функцию isEven для фильтрации нечетных чисел из входного массива с помощью метода filter :
public void filterList_givenIntList_returnResult() { Arrayarray = Array.array(3, 4, 5, 6); Array filteredArray = array.filter(isEven); Array result = Array.array(4, 6); assertTrue(filteredArray.equals(result)); }
Одно интересное наблюдение состоит в том, что в этом примере мы использовали Массив вместо Списка , как мы использовали в предыдущих примерах, и наша функция работала нормально. Из-за того, как абстрагируются и выполняются функции, им не нужно знать, какой метод использовался для сбора входных и выходных данных.
В этом примере мы также использовали нашу собственную функцию isEven , но собственный класс Integer функциональной Java также имеет стандартные функции для базовых числовых сравнений .
7. Применение логической логики с использованием функции
В функциональном программировании мы часто используем логику типа “делайте это только в том случае, если все элементы удовлетворяют некоторому условию” или “делайте это только в том случае, если хотя бы один элемент удовлетворяет некоторому условию”.
Функциональная библиотека Java предоставляет нам ярлыки для этой логики с помощью существует и цветочный методы:
public void checkForLowerCase_givenStringArray_returnResult() { Arrayarray = Array.array("Welcome", "To", "baeldung"); assertTrue(array.exists(s -> List.fromString(s).forall(Characters.isLowerCase))); Array array2 = Array.array("Welcome", "To", "Baeldung"); assertFalse(array2.exists(s -> List.fromString(s).forall(Characters.isLowerCase))); assertFalse(array.forall(s -> List.fromString(s).forall(Characters.isLowerCase))); }
В приведенном выше примере мы использовали массив строк в качестве входных данных. Вызов функции fromString преобразует каждую строку из массива в список символов. К каждому из этих списков мы применили для всех(Characters.is строчные буквы) .
Как вы, вероятно, догадались, Characters.isLowerCase – это функция, которая возвращает значение true, если символ в нижнем регистре. Таким образом, применение для всех(символов.isLowerCase) к списку символов вернет true только в том случае, если весь список состоит из символов нижнего регистра, что, в свою очередь, указывает на то, что исходная строка была полностью строчной.
В первых двух тестах мы использовали существует , потому что мы только хотели знать, была ли хотя бы одна строка строчной. Третий тест использовал для всех , чтобы проверить, все ли строки были строчными.
8. Обработка Необязательных Значений С помощью Функции
Обработка необязательных значений в коде обычно требует == null или Не является пустой проверяет. Java 8 теперь предоставляет класс Необязательно для более элегантной обработки этих проверок, а функциональная библиотека Java предлагает аналогичную конструкцию для изящной обработки недостающих данных с помощью класса Option :
public void checkOptions_givenOptions_returnResult() { Optionn1 = Option.some(1); Option n2 = Option.some(2); Option n3 = Option.none(); F > function = i -> i % 2 == 0 ? Option.some(i + 100) : Option.none(); Option result1 = n1.bind(function); Option result2 = n2.bind(function); Option result3 = n3.bind(function); assertEquals(Option.none(), result1); assertEquals(Option.some(102), result2); assertEquals(Option.none(), result3); }
9. Сокращение набора с помощью функции
Наконец, мы рассмотрим функциональность для уменьшения набора. “Сокращение набора” – это причудливый способ сказать “свернуть его в одно значение”.
Функциональная библиотека Java ссылается на эту функциональность как folding .
Необходимо указать функцию, чтобы указать, что значит складывать элемент. Примером этого является функция Integers.add для отображения целых чисел в массиве или списке, которые необходимо добавить.
В зависимости от того, что делает функция при складывании, результат может отличаться в зависимости от того, начинаете ли вы складывать справа или слева. Вот почему функциональная библиотека Java предоставляет обе версии:
public void foldLeft_givenArray_returnResult() { ArrayintArray = Array.array(17, 44, 67, 2, 22, 80, 1, 27); int sumAll = intArray.foldLeft(Integers.add, 0); assertEquals(260, sumAll); int sumEven = intArray.filter(isEven).foldLeft(Integers.add, 0); assertEquals(148, sumEven); }
Первый foldLeft просто добавляет все целые числа. В то время как второй сначала применит фильтр, а затем добавит оставшиеся целые числа.
10. Заключение
Эта статья-всего лишь краткое введение в функциональную библиотеку Java.
Как всегда, полный исходный код статьи доступен на GitHub .