【Java発展学習3日目】関数インタフェースと関数オブジェクトの扱い方

# 標準関数インタフェース(standard function interface)
[参考: Functional Interface(Java SE 16 & JDK 16)](https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/util/function/package-summary.html)
`関数オブジェクト`を格納するために用意された`インタフェース`であり、**条件に応じて使い分ける**必要がある。

なお、`標準関数インタフェース`の命名規則は以下の通り。

|インタフェース|内容|引数|戻り値|
|:--|:--:|:--:|:--:|
|`*Function`|`型`の`変換`|T|T|
|`*Operator`|`値`の`変換`|T|R|
|`*Consumer`|`値`の`操作`<br>`コンソール出力`|T|- (=`void`)|
|`*Supplier`|`値`の`出力`|-|R|
|`*Predicate`|`条件`の指定|T|`boolean`|

なお、`関数インタフェース(func interface)`を独自に定義する場合も、上記の**命名規則に従う**のが望ましい。

# ラムダ式(lambda expression)
`メソッド`が実体化されるのは「メソッドを定義した**クラスがJVMに読み込まれた時**」であるのに対し、
`ラムダ式`で記述した関数が実体化されるのは「関数の**実行時**」となる。

また、`ラムダ式`は`クロージャ(closure)`を利用することで、
`ラムダ式`の外部で定義された「**実質的に**`final`」な`変数`を参照することができる。

# 関数オブジェクトとラムダ式の有用性
`関数オブジェクト`や`ラムダ式`を用いて「`関数`を`オブジェクト`化する」ことによって、
`処理(アルゴリズム)`を`データ`として受け渡すことが可能となる。

また、`関数インタフェース`の`実装クラスオブジェクト`の代わりに`関数オブジェクト`や`ラムダ式`を用いることができる。

## ラムダ式の省略記法

```java:ラムダ式の省略記法
// 省略記法を用いない場合
IntToDoubleFunction func = (int x) -> {
    return x * x * 3.14;
}

// 代入式の「右辺」では「引数の型省略」が可能
IntToDoubleFunction func = (x) -> {
    return x * x * 3.14;
}

// 引数が「1つ」の場合は「丸括弧」が省略可能
IntToDoubleFunction func = x -> {
    return x * x * 3.14;
}

// ラムダ式が「単一のreturn文」の場合は「波括弧」と「return」が省略可能
IntToDoubleFunction func = x -> x * x * 3.14;
```

## サンプルコード(関数インタフェースとラムダ式)

```java
class MainA {
    public static void main(String[] args) {
        // 「静的メソッド」(=関数オブジェクト)の「ポインタ」を関数インタフェースに格納
        // -> 「静的メソッド」のポインタは"<クラス>::<静的メソッド>"で表現
        TernaryStringToStringFunction<String, String, String, String> staticFunc = FuncInterfaceTest::staticUnion;

        // 動的メソッドの利用にあたってインスタンスを生成
        FuncInterfaceTest fit = new FuncInterfaceTest();

        // 「動的メソッド」(=関数オブジェクト)の「ポインタ」を関数インタフェースに格納
        // -> 「動的メソッド」のポインタは"<クラスオブジェクト>::<動的メソッド>"で表現
        TernaryStringToStringFunction<String, String, String, String> dynamicFunc = fit::dynamicUnion;

        // ラムダ式による関数の記述
        TernaryStringToStringFunction<String, String, String, String> lambdaFunc = 
            (String str1, String str2, String str3) -> { return str1 + str2 + str3; };

        // 関数オブジェクト・ラムダ式関数の呼び出し
        // -> 関数オブジェクト・ラムダ式関数(のポインタ)の格納先は「関数インタフェース」であるため、
        //    "<関数インタフェース>.<抽象メソッド>"で関数オブジェクトを呼び出す
        String staticUnionStr = staticFunc.union("A", "B", "C");
        String dynamicUnionStr = dynamicFunc.union("1", "2", "3");
        String lambdaUnionStr = lambdaFunc.union("あ", "い", "う");

        System.out.println("Static Method: " + staticUnionStr);
        System.out.println("Dynamic Method: " + dynamicUnionStr);
        System.out.println("Lambda Method: " + lambdaUnionStr);
    }
}

class FuncInterfaceTest {
    // 静的メソッド
    public static String staticUnion(String str1, String str2, String str3) {
        return str1 + str2 + str3;
    }
    // 動的メソッド
    public String dynamicUnion(String str1, String str2, String str3) {
        return str1 + str2 + str3;
    }
}

// 独自定義した関数インタフェース
// -> "@FunctionalInterface"アナテイションを付与することで、「関数インタフェース」であることを明示的に宣言
// -> 「関数インタフェース」であるため、「ただ1つの抽象メソッド」のみを定義
@FunctionalInterface
interface TernaryStringToStringFunction<F, S, T, R> {
    public abstract String union(String str1, String str2, String str3);
}
```

```java:実行結果
Static Method: ABC
Dynamic Method: 123
Lambda Method: あいう
```

***

# Streamインタフェース(=Stream API)
`ストリーム(stream)`に対して`宣言的(declarative)`に処理を行う`高階関数(high-order function)`をまとめた`インタフェース`。
`ストリーム`は`処理データ`を格納した`コレクション`や`配列`を通じて生成され、`ポインタ`に格納された`処理データ`を**直接**操作することができる。

なお、`Stream`インタフェースは`AutoCloseable`インタフェースを継承する`BaseStream`インタフェースを継承しており、
`ストリーム`が生成されてから**一度でも**返却型が`Stream<T>`型でない`終端処理メソッド`が実行されると、
自動的に`BaseStream`インタフェースの`close()`メソッドが呼び出される。

ただし、返却型が`Stream<T>`型である`中間処理メソッド`は`ストリーム`を`クローズ`しない。

# コレクション(collection)/配列(array)/マップ(map)
[参考1: コレクション](https://qiita.com/suema0331/items/637ff9457a371069a7dc)
[参考2: ArrayListとLinkedListの違い](https://qiita.com/kenRp01/items/b62d7b22fe7f482599e9)
[参考3: ArrayListとLinkedListの使い分け](https://qiita.com/BumpeiShimada/items/522798a380dc26c50a50)
[参考4: Java発展学習1日目](https://qiita.com/b150005/items/0a8cc4086fc2ddba7cb4#%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3collection%E3%81%A8%E9%85%8D%E5%88%97arrays)

## コレクション(collection)

![Collection.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/673880/21a04a70-c787-7263-669a-00e7adee631b.png)

## 配列(array)

![Array.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/673880/12460fe3-237f-86b2-5207-81f2aaa3fccf.png)

## マップ(map)

![Map.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/673880/4964b2c2-5da4-22c2-1b8a-1dc9ef0cb633.png)

## 定義(ストリームの生成)

```java:ストリームの生成
// コレクションからストリームを生成
Stream<E> Collection<E>.stream()

// 配列からストリームを生成
Stream<T> Arrays.stream(T[] array)
// パラメータ
// array: T型データの配列

// 「可変長引数」で構成要素を指定してストリームを生成
@SafeVarargs Stream<T> Stream.of(T... values)
// パラメータ
// values: ストリームのT型構成要素
```

## 定義(中間処理メソッド)

```java:中間処理メソッド
// 要素の値に「重複」がないストリームを返却
Stream<T> Stream<T>.distinct()

// 「条件」を表す関数を満たす要素で構成されるストリームを返却
Stream<T> Stream<T>.filter(Predicate<? super T> predicate)
// パラメータ
// predicate: 「条件」を表すPredicate型の関数

// 要素数を「制限」したストリームを返却
Stream<T> Stream<T>.limit(long maxSize)
// パラメータ
// maxSize: 「要素数」の上限値

// 「自然順序付け」されたストリームを返却
Stream<T> Stream<T>.sorted()

// 関数の「戻り値」で構成されたストリームを返却
Stream<R> Stream<T>.map(Function<? super T, ? extends R> mapper)
// パラメータ
// mapper: 「型の変換」を表すFunction型の関数

// 順次ストリーム -> 並列ストリーム への変換
// -> Stream<T>はBaseStream<T, Stream<T>>を継承
S BaseStream<T, S extends BaseStream<T, S>>.parallel()
```

## 定義(終端処理メソッド)

```java:終端処理メソッド
// 「全要素」が「条件」を表す関数を満たしている場合はtrueを返却
// ※ストリームが「空」である場合もtrueが返却される
boolean Stream<T>.allMatch(Predicate<? super T> predicate)
// パラメータ
// predicate: 「条件」を表すPredicate型の関数

// 「少なくとも1要素」が「条件」を表す関数を満たしている場合はtrueを返却
// ※ストリームが「空」である場合もtrueが返却される
boolean Stream<T>.anyMatch(Predicate<? super T> predicate)
// パラメータ
// predicate: 「条件」を表すPredicate型の関数

// 「全要素」が「条件」を表す関数を満たしていない場合はtrueを返却
// ※ストリームが「空」である場合もtrueが返却される
boolean Stream<T>.noneMatch(Predicate<? super T> predicate)
// パラメータ
// predicate: 「条件」を表すPredicate型の関数

// 「各要素」に関数を適用
void Stream<T>.forEach(Consumer<? super T> action)
// パラメータ
// action: 「値の操作」または「コンソール出力」を行うConsumer型の関数

// ストリームの「最初の要素」を返却
Optional<T> Stream<T>.findFirst()

// ストリームの「最初に見つかった要素」を返却
// -> 「マルチスレッド」でストリームを処理する場合(=並列ストリーム)は結果が異なる"可能性"がある
// => スレッドの負荷が大きい場合は必ずしも「最初の要素」が返却されるというわけではない
Optional<T> Stream<T>.findAny()

// ストリームの「要素数」を返却
long Stream<T>.count()

// 関数(=コンパレータ)を満たす「最大値の要素」を返却
Optional<T> Stream<T>.max(Comparator<? super T> comparator)
// パラメータ
// comparator: 「比較対象」を表すComparator型のコンパレータ

// 関数(=コンパレータ)を満たす「最大値の要素」を返却
Optional<T> Stream<T>.min(Comparator<? super T> comparator)
// パラメータ
// comparator: 「比較対象」を表すComparator型のコンパレータ
```

## サンプルコード
[参考1: findFirst()とfindAny()](https://codechacha.com/ja/java8-stream-difference-findany-findfirst/)
[参考2: Stream API](https://qiita.com/KO_YAmajun/items/f61ed6703e0443c4a3c3#21-illegalstateexception%E3%81%8C%E7%99%BA%E7%94%9Fstream%E3%81%8C%E5%86%8D%E5%88%A9%E7%94%A8%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84)

```java:DeclarativeStream.java
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

public class DeclarativeStream {
    public static void main(String[] args) {
        // ストリームの構成要素をリスト(コレクション)で定義
        List<CompareAWithB> list = new ArrayList<>();
        list.add(new CompareAWithB("A", 9));
        list.add(new CompareAWithB("B", 7));
        list.add(new CompareAWithB("C", 12));

        // コレクションからストリームを生成
        Stream<CompareAWithB> stm = list.stream();
        // ストリームの要素数
        // -> Streamは一度でも「終端処理」が行われるとクローズされるため、再利用不可
        long c = stm.count();
        System.out.println("components in Stream: " + c);

        // プロパティiの値が10未満である各要素に対して行う処理
        list.stream()
            // 中間処理
            .filter( i -> i.getInt() < 10)
            // 終端処理
            .forEach(i -> i.addString("(less than 10)"));

        // Optional<T>型は「Optional.empty」で初期化される
        // <- 「ローカル変数」は初期化必須
        Optional<CompareAWithB> comOpt;
        CompareAWithB com = new CompareAWithB();

        System.out.println("-- findAny() by Multi Thread--");
        for (int i = 0; i < 100; i++) {
            // 順次ストリーム -> 並列ストリーム への変換
            Stream<CompareAWithB> palStm = list.stream().parallel();

            // 並列ストリームで最初に見つかった要素を返却
            comOpt = palStm.findAny();

            // Optional<T>型を取り扱う際はnull安全に配慮
            if (comOpt.isPresent()) {
                // Optional<CompareAWithB>型 -> CompareAWithB型 への変換
                com = comOpt.get();
            }
            if (com != null) {
                System.out.println( (i + 1) + ": " + com );
            }
        }

        // 全要素のプロパティiの値が3より大きいである場合の処理
        if (list.stream().allMatch( (CompareAWithB i) -> {
            return i.getInt() > 3;
        })) {
            System.out.println("All components have properties with 3 or greater.");
        }

        // プロパティiの値が最小である要素
        // -> 「比較対象」(=コンパレータ)を指定
        Optional<CompareAWithB> minCon = list.stream().min((x, y) -> x.getInt() - y.getInt());

        System.out.println("component with the minimum value: " + minCon.get());
    }
}

class CompareAWithB {
    private String str;
    private int i;

    // デフォルトコンストラクタ
    public CompareAWithB() {}

    // コンストラクタ
    public CompareAWithB(String str, int i) {
        this.str = str;
        this.i = i;
    }

    // ゲッタ(=アクセサ)
    public String getString() {
        return this.str;
    }
    public int getInt() {
        return this.i;
    }

    // 文字列を追加
    public void addString(String s) {
        this.str += s;
    }

    @Override
    public String toString() {
        return "CompareAWithB(" + this.str + ", " + this.i + ")";
    }
}
```

```java:実行結果(一部改変)
components in Stream: 3
-- findAny() by Multi Thread--
1: CompareAWithB(B(less than 10), 7)
...
13: CompareAWithB(A(less than 10), 9)
14: CompareAWithB(C, 12)
...
All components have properties with 3 or greater.
component with the minimum value: CompareAWithB(B(less than 10), 7)
```

***

# 用語集

|用語|内容|
|:--|:--|
|第1級オブジェクト(first-class object)|`基本的操作`(=`生成`・`代入`・`演算`・`受け渡し`など)を**制限なく**実行できるもの。|
|関数(function)|`入力(input)`を受け取り、`処理(process)`を行った後に実行結果を`出力(output)`するもの。<br>=`IPO構造`を伴う`処理ロジック`。|
|関数インタフェース(func interface)|**1つの**`抽象メソッド`のみを有する(=`SAM; Single Abstract Method`)`インタフェース`。<br>`@FunctionalInterface`アナテイションを用いて明示的に宣言することができる。|
|クロージャ(closure)|`関数`の実体化時に、その時点で`アクセス可能`な変数を`参照`できる特性。|
|高階関数(high-order function)|引数として`関数`を受け取る関数。|
|宣言的(declarative)|`目的`のみを記述し、その`方法`を記述しないこと。|
|ストリーム(stream)|`宣言的`に処理を行うための`データ`の並び。|
|順次ストリーム(sequential stream)|`シングルスレッド`で処理される`ストリーム`。|
|並列ストリーム(parallel stream)|`マルチスレッド`で処理される`ストリーム`。|
|手続き型プログラミング(procedural programming)|`プログラム`を「順番に実行される`命令文`の集まり」と捉えるプログラミング手法。|
|オブジェクト指向プログラミング(object-oriented programming)|`プログラム`を「責務をもつ`部品`の集まり」と捉えるプログラミング手法。|
|関数型プログラミング(functional programming)|`プログラム`を「`高階関数`の集まり」と捉えるプログラミング手法。|