Рубрики
Без рубрики

Введение в функциональную Java

Откройте для себя основы функциональной библиотеки Java

Автор оригинала: baeldung.

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 F timesTwo = i -> i * 2;

Выше мы видим пример F интерфейс, который принимает Целое число в качестве входных данных и возвращает, что Целое число умножьте два раза на его выход.

Вот еще один пример базовой функции, которая принимает Целое число в качестве входных данных, но в этом случае возвращает Логическое значение , чтобы указать, был ли ввод четным или нечетным:

public static final F isEven = i -> i % 2 == 0;

5. Применение функции

Теперь, когда у нас есть наши функции, давайте применим их к набору данных.

Функциональная библиотека Java предоставляет обычный набор типов для управления данными, такими как списки, наборы, массивы и карты. Главное, что нужно понять, – это то, что эти типы данных неизменяемы.

Кроме того, библиотека предоставляет удобные функции для преобразования в стандартные классы коллекций Java и из них при необходимости.

В приведенном ниже примере мы определим список целых чисел и применим к нему нашу функцию timesTwo . Мы также вызовем map , используя встроенное определение той же функции. Конечно, мы ожидаем, что результаты будут такими же:

public void multiplyNumbers_givenIntList_returnTrue() {
    List fList = 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() {
    List fList = 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() {
    List fList = 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() {
    Array array = 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() {
    Array array = 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() {
    Option n1 = 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() {
    Array intArray = 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 .