主要な関数型インタフェースと役割

## はじめに
本記事ではJavaのラムダ式および関数型インタフェースについて、主要な関数型インタフェースとその具体例について説明していきます。

ラムダ式の基本とその背景について知りたい方は、こちらの記事を参考にしてください。
[ラムダ式の基本とその背景](https://qiita.com/pomecode/private/c1bcef34c6c6f48e311d)

## 対象者
・ラムダ式および関数型インタフェースを初めて学ぶ初心者
・基礎的な知識を習得済みで、再確認や実践例を通じて理解を深めたい方

## 関数型インタフェースの実践例
### 主要な関数型インタフェースの種類とその使い方
Java SEでは汎用性の高い関数型インタフェースがクラスライブラリとしてあらかじめ用意されています。
これらを使用すれば、複雑な処理を除いて開発者自身で関数型インタフェースを定義する必要がなくなります。
特に主要なものは以下の通り。

**【主要な関数型インタフェース】**
|分類|関数型インタフェース|抽象メソッド|説明|
|--|--|--|--|
|引数なし、戻り値なし|Runnable|void run()|任意の処理を実行する|
|引数なし、戻り値あり|Supplier\<T>|T get()|任意の処理を実行し、結果を返す|
|引数あり、戻り値なし|Consumer\<T>|void accept(T)|指定された引数で、任意の処理を実行する（戻り値なし）|
||BiConsumer\<T,U>|void accept(T, U)|指定された2つの引数で、任意の処理を実行する（戻り値なし）|
|引数あり、戻り値あり|Function\<T,R>|R apply(T)|指定された引数で、任意の処理を実行し、結果を返す|
||BiFunction\<T,U,R>|R apply(T, U)|指定された2つの引数で、任意の処理を実行し、結果を返す|
||Predicate\<T>|boolean test(T)|指定された引数で、任意の判定処理を行い、結果をboolean型で返す|
||BiPredicate\<T,U>|boolean test(T, U)|指定された2つの引数で、任意の判定処理を行い、結果をboolean型で返す|
||UnaryOperator\<T>|T apply(T)|指定された引数に対して何らかの演算処理を行い、その結果を返す。<br>※引数と戻り値が同一のFunctionインタフェースと同様|
||BinaryOperator\<T>|T apply(T, T)|指定された2つの引数に対して何らかの演算処理を行い、その結果を返す。<br>※2つの引数と戻り値がすべて同一のBiFunctionインタフェースと同様|

今回は上記のうち、`Runnable`、`Supplier`、`Consumer`、`Function`、`Predicate`の5つについて具体例を交えて触れてみたいと思います。
尚、本記事では具体例について触れませんが、`BiConsumer`や`BiFunction`など、引数を2つ取るインタフェースもあらかじめ用意されています。

#### Runnableインタフェース
`Runnable`インタフェースは「引数なし、戻り値なし」の関数型インタフェースです。
主にスレッドプログラミングで使用されますが、それ以外のケースでも使用可能です。

以下は`Runnable`インタフェースをラムダ式で実装し、`Hello, World!`を出力する処理を代入しています。
`Runnable`インタフェースの`run`メソッドを実行することで、処理が行われます。

**【Runnableの具体例①】**
```java
public class RunnableExample1 {
    public static void main(String[] args) {
    	Runnable task = () -> System.out.println("Hello, World!");
    	task.run();
    }
}
```

**【出力結果】**
```
Hello, World!
```

以下は複数のスレッドを使用して、複数のタスクを実行する例になります。

**【Runnableの具体例②】**
```java
public class RunnableExample2 {
    public static void main(String[] args) {
        // Runnableを実装したタスク1
        Runnable task1 = () -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("Task 1: Count " + i);
                try {
                    Thread.sleep(400); // 0.4秒間停止
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        // Runnableを実装したタスク2
        Runnable task2 = () -> {
            for (int i = 0; i < 5; i++) {
                System.out.println("Task 2: Count " + i);
                try {
                    Thread.sleep(600);// 0.6秒間停止
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        // Threadにタスクを登録して実行
        Thread thread1 = new Thread(task1);
        Thread thread2 = new Thread(task2);

        thread1.start();
        thread2.start();
    }
}
```

**【出力結果】**
```
Task 1: Count 0
Task 2: Count 0
Task 1: Count 1
Task 2: Count 1
Task 1: Count 2
Task 2: Count 2
Task 1: Count 3
Task 1: Count 4
Task 2: Count 3
Task 2: Count 4
```
※スレッドの実行順序やタイミングは保証されません。（スケジューリングによるため）

#### Supplierインタフェース
`Supplier`インタフェースは「引数なし、戻り値あり」の関数型インタフェースです。
戻り値を一つ返すため、様々な値を取得する際に使用されます。

以下は`Supplier`インタフェースをラムダ式で実装し、`Hello, World!`を出力する処理を代入しています。
`Supplier`インタフェースの`get`メソッドを実行することで、処理が行われます。

**【Supplierの具体例①】**
```java
public class SupplierExample1 {
    public static void main(String[] args) {
    	Supplier<String> stringSupplier = () -> "Hello, World!";
    	String s = stringSupplier.get();
    	System.out.println(s);
    }
}
```

**【出力結果】**
```
Hello, World!
```

以下は現在時刻を取得する例になります。

**【Supplierの具体例②】**
```java
public class SupplierExample2 {
    public static void main(String[] args) {
        Supplier<Long> currentTimeSupplier = () -> System.currentTimeMillis();
        Long currentTime = currentTimeSupplier.get();
        System.out.println("Current Time: " + currentTime);
    }
}
```

**【出力結果】**
```
Current Time: 1743750478216
```

#### Consumerインタフェース
`Consumer`インタフェースは「引数あり、戻り値なし」の関数型インタフェースです。
引数は一つ受け取りますが、戻り値は返さないため、何かしらの副作用を発生させるために使用されます。

以下は`Consumer`インタフェースをラムダ式で実装し、変数：`message`を出力する処理を代入しています。
`Consumer`インタフェースの`accept`メソッドを実行することで、処理が行われます。

**【Consumerの具体例①】**
```java
public class ConsumerExample1 {
    public static void main(String[] args) {
        Consumer<String> c = message -> System.out.println(message);
        c.accept("Hello, World!");
    }
}
```

**【出力結果】**
```
Hello, World!
```

以下はリスト内の各要素を大文字に変換して出力する例です。
`Iterable`インタフェースの`forEach`メソッドに`Consumer`型の変数：`printUpperCase`を渡していますが、これは`Iterable`インタフェースに`forEach`メソッドがデフォルト実装として提供されており、引数として`Consumer`を受け取るように設計されているためです。

**【Consumerの具体例②】**
```java
public class ConsumerExample2 {
    public static void main(String[] args) { 
        List<String> cities = Arrays.asList("Tokyo", "Osaka", "Chiba");
        Consumer<String> printUpperCase = city -> System.out.println(city.toUpperCase());
        cities.forEach(printUpperCase);
    }
}
```

**【出力結果】**
```
TOKYO
OSAKA
CHIBA
```

#### Functionインタフェース
`Function`インタフェースは「引数あり、戻り値あり」の関数型インタフェースです。
引数を一つ受け取り、戻り値も一つ返すため、数値の計算結果の取得や文字列の長さの取得等に使用できます。

以下は`Function`インタフェースをラムダ式で実装し、`apply`メソッドを実行することで商品の価格から消費税込みの価格を計算しています。
`Function<T,R>`の`T`は引数、`R`は戻り値ですので、以下の場合はともに`Double`型を指定しています。

**【Functionの具体例①】**
```java
public class FunctionExample1 {
    public static void main(String[] args) {
    	double taxRate = 1.08;
        Function<Double, Double> taxFunction = (price) -> price * taxRate;
        double price = 1000.0;
        double taxPrice = taxFunction.apply(price);
        System.out.println(taxPrice);
    }
}
```

**【出力結果】**
```
1080.0
```

以下は`String`型の文字列を受け取り、その長さを返す例です。
返す値は数値のため、戻り値の方は`Integer`型を指定しています。

**【Functionの具体例②】**
```java
public class FunctionExample2 {
    public static void main(String[] args) {
        Function<String, Integer> lengthFunction = (String str) -> str.length();
        Integer length = lengthFunction.apply("Hello");
        System.out.println(length);
    }
}
```

**【出力結果】**
```
5
```

#### Predicateインタフェース
`Predicate`インタフェースは「引数あり、戻り値あり」の関数型インタフェースです。
引数を一つ受け取り、何らかの処理を行った上で結果を`boolean`型で返却します。

以下の例では数値が偶数かどうかを判定しています。
`Predicate`インタフェースの`test`メソッドを実行することで判定を行っています。

**【Predicateの具体例①】**
```java
public class PredicateExample1 {
    public static void main(String[] args) {
        Predicate<Integer> isEven = n -> n % 2 == 0;
        System.out.println(isEven.test(4));
        System.out.println(isEven.test(7));
    }
}
```

**【出力結果】**
```
true
false
```

また、以下の例では文字列が空かどうかを判定しています。

**【Predicateの具体例②】**
```java
public class PredicateExample2 {
    public static void main(String[] args) {
        Predicate<String> isEmpty = str -> str.isEmpty();
        System.out.println(isEmpty.test(""));
        System.out.println(isEmpty.test("Hello"));
    }
}
```

**【出力結果】**
```
true
false
```

#### プリミティブ型のインタフェース
`java.util.function`パッケージには、引数や戻り値が特定のプリミティブ型に特化した専用のインタフェースが用意されています。
本記事では具体例について触れませんが、以下に代表的なものを示しておきます。

**Supplierインタフェースの特化型**
|引数|戻り値|インタフェース|
|--|--|--|
|なし|int型|IntSupplier|
|なし|long型|LongSupplier|
|なし|double型|DoubleSupplier|

**Consumerインタフェースの特化型**
|引数|戻り値|インタフェース|
|--|--|--|
|int型|なし|IntConsumer|
|long型|なし|LongConsumer|
|double型|なし|DoubleConsumer|

**Functionインタフェースの特化型**
|引数|戻り値|インタフェース|
|--|--|--|
|int型|総称型|IntFunction\<R>|
|long型|総称型|LongFunction\<R>|
|double型|総称型|DoubleFunction\<R>|
|総称型|int型|ToIntFunction\<T>|
|総称型|long型|ToLongFunction\<T>|
|総称型|double型|ToDoubleFunction\<T>|

**Predicateインタフェースの特化型**
|引数|戻り値|インタフェース|
|--|--|--|
|int型|boolean型|IntPredicate|
|long型|boolean型|LongPredicate|
|double型|boolean型|DoublePredicate|