1. 概要

このチュートリアルでは、機能Javaライブラリの概要といくつかの例を紹介します。

2. 機能的なJavaライブラリ

関数型Javaライブラリは、Javaでの関数型プログラミングを容易にすることを目的としたオープンソースライブラリです。 このライブラリは、関数型プログラミングで一般的に使用される多くの基本的および高度なプログラミング抽象化を提供します。

ライブラリの機能の多くは、Fインターフェイスを中心に展開されます。 このFインターフェースは、タイプAの入力を受け取り、タイプBの出力を返す関数をモデル化します。これらはすべて、Java独自の型システムの上に構築されています。

3. Mavenの依存関係

まず、必要な依存関係pom.xmlファイルに追加する必要があります。

<dependency>
    <groupId>org.functionaljava</groupId>
    <artifactId>functionaljava</artifactId>
    <version>4.8.1</version>
</dependency>
<dependency>
    <groupId>org.functionaljava</groupId>
    <artifactId>functionaljava-java8</artifactId>
    <version>4.8.1</version>
</dependency>
<dependency>
    <groupId>org.functionaljava</groupId>
    <artifactId>functionaljava-quickcheck</artifactId>
    <version>4.8.1</version>
</dependency>
<dependency>
    <groupId>org.functionaljava</groupId>
    <artifactId>functionaljava-java-core</artifactId>
    <version>4.8.1</version>
</dependency>

4. 関数の定義

後で例で使用できる関数を作成することから始めましょう。

Functional Javaがない場合、基本的な乗算方法は次のようになります。

public static final Integer timesTwoRegular(Integer i) {
    return i * 2;
}

Functional Javaライブラリを使用すると、この機能をもう少しエレガントに定義できます。

public static final F<Integer, Integer> timesTwo = i -> i * 2;

上記の例は、 Integer を入力として受け取り、Integerの2倍を出力として返すFインターフェースの例です。

Integer を入力として受け取る基本関数の別の例を次に示しますが、この場合、 Boolean を返し、入力が偶数か奇数かを示します。

public static final F<Integer, Boolean> isEven = i -> i % 2 == 0;

5. 関数の適用

関数が配置されたので、それらをデータセットに適用しましょう。

Functional Javaライブラリは、リスト、セット、配列、マップなどのデータを管理するための通常のタイプのセットを提供します。 認識すべき重要なことは、これらのデータ型は不変であるということです。

さらに、ライブラリは、必要に応じて標準のJavaコレクションクラスとの間で変換するための便利な関数を提供します。

以下の例では、整数のリストを定義し、それにtimesTwo関数を適用します。 同じ関数のインライン定義を使用して、mapも呼び出します。 もちろん、結果は同じであると期待しています。

public void multiplyNumbers_givenIntList_returnTrue() {
    List<Integer> fList = List.list(1, 2, 3, 4);
    List<Integer> fList1 = fList.map(timesTwo);
    List<Integer> fList2 = fList.map(i -> i * 2);

    assertTrue(fList1.equals(fList2));
}

ご覧のとおり、 map は同じサイズのリストを返します。ここで、各要素の値は、関数が適用された入力リストの値です。 入力リスト自体は変更されません。

isEven関数を使用した同様の例を次に示します。

public void calculateEvenNumbers_givenIntList_returnTrue() {
    List<Integer> fList = List.list(3, 4, 5, 6);
    List<Boolean> evenList = fList.map(isEven);
    List<Boolean> evenListTrueResult = List.list(false, true, false, true);

    assertTrue(evenList.equals(evenListTrueResult));
}

mapメソッドはリストを返すため、その出力に別の関数を適用できます。 map 関数を呼び出す順序によって、結果の出力が変わります。

public void applyMultipleFunctions_givenIntList_returnFalse() {
    List<Integer> fList = List.list(1, 2, 3, 4);
    List<Integer> fList1 = fList.map(timesTwo).map(plusOne);
    List<Integer> fList2 = fList.map(plusOne).map(timesTwo);

    assertFalse(fList1.equals(fList2));
}

上記のリストの出力は次のようになります。

List(3,5,7,9)
List(4,6,8,10)

6. 関数を使用したフィルタリング

関数型プログラミングでよく使用されるもう1つの操作は、入力を取得し、いくつかの基準に基づいてデータをフィルターで除外することです。 そして、おそらくすでにお察しのとおり、これらのフィルタリング基準は関数の形式で提供されます。 この関数は、データを出力に含める必要があるかどうかを示すブール値を返す必要があります。

次に、 isEven 関数を使用して、filterメソッドを使用して入力配列から奇数をフィルターで除外します。

public void filterList_givenIntList_returnResult() {
    Array<Integer> array = Array.array(3, 4, 5, 6);
    Array<Integer> filteredArray = array.filter(isEven);
    Array<Integer> result = Array.array(4, 6);

    assertTrue(filteredArray.equals(result));
}

興味深い観察の1つは、この例では、前の例で使用したListの代わりにArrayを使用し、関数が正常に機能したことです。 関数が抽象化および実行される方法のため、入力と出力を収集するためにどのメソッドが使用されたかを認識する必要はありません。

この例では、独自の isEven 関数も使用しましたが、FunctionalJava独自のInteger クラスには、基本的な数値比較の標準関数もあります。

7. 関数を使用したブール論理の適用

関数型プログラミングでは、「すべての要素が何らかの条件を満たす場合にのみこれを行う」、「少なくとも1つの要素が何らかの条件を満たす場合にのみこれを行う」などのロジックを頻繁に使用します。

Functional Javaライブラリは、examplesおよびforallメソッドを介してこのロジックのショートカットを提供します。

public void checkForLowerCase_givenStringArray_returnResult() {
    Array<String> array = Array.array("Welcome", "To", "baeldung");
    assertTrue(array.exists(s -> List.fromString(s).forall(Characters.isLowerCase)));

    Array<String> 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 関数を呼び出すと、配列の各文字列が文字のリストに変換されます。 これらの各リストに、 forall(Characters.isLowerCase)を適用しました。

ご想像のとおり、 Characters.isLowerCase は、文字が小文字の場合にtrueを返す関数です。 したがって、 forall(Characters.isLowerCase)を文字のリストに適用すると、リスト全体が小文字で構成されている場合にのみ true が返されます。これは、元の文字列がすべて小文字であることを示します。 。

最初の2つのテストでは、少なくとも1つの文字列が小文字であるかどうかだけを知りたいので、examplesを使用しました。 3番目のテストでは、 forall を使用して、すべての文字列が小文字であるかどうかを確認しました。

8. 関数を使用したオプション値の処理

コードでオプションの値を処理するには、通常、 ==nullまたはisNotBlankチェックが必要です。 Java 8は、これらのチェックをよりエレガントに処理するためのオプションクラスを提供し、Functional Javaライブラリは、Optionクラスを介して欠落データを適切に処理するための同様の構造を提供します。

public void checkOptions_givenOptions_returnResult() {
    Option<Integer> n1 = Option.some(1);
    Option<Integer> n2 = Option.some(2);
    Option<Integer> n3 = Option.none();

    F<Integer, Option<Integer>> function = i -> i % 2 == 0 ? Option.some(i + 100) : Option.none();

    Option<Integer> result1 = n1.bind(function);
    Option<Integer> result2 = n2.bind(function);
    Option<Integer> result3 = n3.bind(function);

    assertEquals(Option.none(), result1);
    assertEquals(Option.some(102), result2);
    assertEquals(Option.none(), result3);
}

9. 関数を使用して集合を減らす

最後に、セットを減らすための機能について見ていきます。 「セットを減らす」とは、「1つの値にまとめる」という言い方です。

機能Javaライブラリは、この機能を折りたたみと呼びます。

要素を折りたたむことの意味を示す関数を指定する必要があります。 この例は、 Integers.add 関数で、配列またはリスト内の整数を追加する必要があることを示します。

折りたたむときの関数の動作によって、右から折りたたむか左から折りたたむかによって結果が異なる場合があります。 これが、FunctionalJavaライブラリが両方のバージョンを提供する理由です。

public void foldLeft_givenArray_returnResult() {
    Array<Integer> 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は、単にすべての整数を加算します。 一方、2番目は最初にフィルターを適用し、次に残りの整数を追加します。

10. 結論

この記事は、FunctionalJavaライブラリの簡単な紹介です。

いつものように、記事の完全なソースコードは、GitHubから入手できます。