【第二回】Stream APIを使う前に関数型インターフェースを理解する

#はじめに
この投稿は、Java8におけるStream APIを活用する為の事前知識として書いています。
Streamを理解するためには複数の知識が必要なので、下記流れで投稿していこうと思っています！
今回はその二回目です！

* 第一回　[ジェネリクス](https://qiita.com/tech_newbie/items/6adcb7dc5f59165f3fb9)
* 第二回　[関数型インターフェース](https://qiita.com/tech_newbie/items/083cf1ffcfa9744b0c88)
* 第三回　[ラムダ式とメソッド参照](https://qiita.com/tech_newbie/items/2588970f3d2ce08a1b16)
* 第四回　[Stream API](https://qiita.com/tech_newbie/items/3ee76c639f6a1a311174)

#Java8におけるインターフェース
関数型インターフェースに入る前に、Java7までのインターフェースとJava8になってからのインターフェースの違いを確認します。

|要素|Java7までのインターフェース|Java8からのインターフェース|
|:---|:---|:---|
|抽象メソッド|複数定義可能|複数定義可能|
|staticメソッド|定義不可|複数定義可能|
|defaultメソッド|定義不可|複数定義可能|
|オブジェクト生成|生成不可|生成可能|

Java7まではインターフェースに実装を持つことはできませんでしたが、
Java8以降はstaticメソッドやdefaultメソッドを定義することが可能になっています。

#関数型インターフェースの定義
ここから本題の関数型インターフェースです。
まずは関数型インターフェースの定義を見てみましょう。

|要素|関数型インターフェース|
|:---|:---|
|抽象メソッド|**1つのみ**|
|staticメソッド|複数定義可能|
|defaultメソッド|複数定義可能|
|オブジェクト生成|生成可能|

ここでのポイントは**「抽象メソッドは1つしか持つことができない」**ということです。
通常のインターフェースと同様にstaticメソッドやdefaultメソッドは複数持っても構いませんが、
抽象メソッドは1つしか持つことはできません。


#関数型インターフェースの利用箇所
* 無名クラスを渡す時
* ラムダ式やメソッド参照を渡す時（代入先となる時）
* Streamの処理において引数として渡す時

正直なところ、これだけじゃイメージつかないですよね？
それなので、ラムダ式に関しては第三回の投稿で説明します。
Streamに関しては第四回の投稿で説明します。

この段階では関数型インターフェースとは

* **実装すべき抽象メソッドが一つしか定義されていないインターフェース**
* **1つのメソッドしか記述できず、状態を持てないという制約（インターフェースであるという性質）を課すことで、副作用のない関数を実装できる**
* **関数とは入力と出力(引数と戻り値)が定義されており、それ自体が独立して呼び出しできるもの**

といったイメージだけ抑えておけばいいのかなと思います。

#関数型インターフェースの使い方

前置きがだいぶ長くなりました。
イメージを掴むには実際の使用例を見た方が早いですね！

まずは関数型インターフェースの定義からです

```java:SampleIf.java

@FunctionalInterface
public interface SampleIf {
	public abstract Integer countLength(String str);
}
```
これは、String型の引数を受け取って、戻り値としてInteger型を返す抽象メソッド(countLength)を持つ関数型インターフェースです。
（実装としては文字列を受け取って、その文字列の数を数える形にしていきます）
抽象メソッドなのでここでは当然実装していません。



次に実装を行います。
今回は無名クラスを用いて実装します。

```java:Sample.java

public static void main(String[] args) {
	SampleIf count = new SampleIf() {
		//抽象メソッド(countLength)を実装
		@Override
		public Integer countLength(String str) {
			return str.length();
		};
	};
	//実行
	System.out.println("カウント結果：" + count.countLength("テスト")); // -> カウント結果：3
}
```
引数として文字列を受け取って、その文字数を返すだけの実装です。

わざわざ関数型インターフェースを定義し、実装クラスでnewして抽象メソッドをOverride（実装）しています。
**~~正直面倒ですよね？~~**

これは**ラムダ式を用いることで遥かに簡単に記述することができますが**、ラムダ式は[第三回]((https://qiita.com/tech_newbie/items/2588970f3d2ce08a1b16))の投稿に任せる為、現時点ではあえて無名クラスを使った冗長な記述にしています。

ここではラムダ式の説明は行わないことで、関数型インターフェースそのものにフォーカスを当てていければと思います。

#＠FunctionalInterface
ところで、先程のコードに**`@FunctionalInterface`**という記述が出てきました。
このアノテーションは付けなくても問題はないですが、付けることでこのインターフェースは関数型インターフェースの条件を満たしているということを明示することができます（仮に満たしていない場合はコンパイルエラーが出ます）。
コンパイル時に検証することができるので、基本的に付けるようにしたいですね！

#java.util.functionパッケージを使う

ここまではSampleIfという独自の関数型インターフェースを定義し、実装を行ってきました。
実は**これだけの処理であれば独自に定義する必要はありません。**

というのも、**Java8からは便利で汎用的な関数型インターフェースが既に用意されているためです。**

この、汎用的に使える関数型インターフェースは**`java.util.function`**パッケージで用意されていて、importするだけで利用することができます。

試しに**`Function`**という関数型インターフェースをJava APIリファレンスで見てみましょう。

関数型インターフェースの宣言
>@FunctionalInterface
>public interface Function\<T,R>

抽象メソッド
>R apply(T t)

ここからわかることは

- **`@FunctionalInterface`**が付いていることからもわかるように関数型インターフェースであり
- **`T`**と**`R`**という仮型パラメータを使用し
- 1つしか定義されていない抽象メソッド(apply)は**T型の引数を1つ受け取り、R型の戻り値を返す**ということです

※**T,Rって何？**という方は[第一回](https://qiita.com/tech_newbie/items/6adcb7dc5f59165f3fb9)の投稿をご確認下さい！

実はこの関数型インターフェース(Function)は、上記で独自に定義した関数型インターフェース「SampleIf」と**全く同じ処理内容です。**

SampleIfはこのような関数型インターフェースでした

- **`@FunctionalInterface`**が付いていることからもわかるように関数型インターフェースであり
- **`String`**と**`Integer`**という型を使用し
- 1つしか定義されていない抽象メソッド(countLength)は**String型の引数を1つ受け取り、Integer型の戻り値を返す**

一緒ですよね？

要は**独自に関数型インターフェースを定義する必要は全くありませんでした...**
**`Function`**を使えばよかったのです。

実際に使ってみましょう。

```java:FuncSample.java

public static void main(String[] args) {
	Function<String, Integer> countFnc = new Function<String, Integer>() {
		//関数型インターフェースFunctionの抽象メソッド(apply)を実装
		@Override
		public Integer apply(String str) {
			return str.length();
		};
	};
	//実行
	System.out.println("カウント結果：" + countFnc.apply("テスト")); // -> カウント結果：3
}
```

処理結果は上記で独自に定義した関数型インターフェースと変わらないことが確認できるかと思います。

#java.util.function内で覚えるべき関数型インターフェース

**`Function`**のように**`java.util.function`**内には既に汎用的に使える関数型インターフェースが準備されています。
その中からよく使われるものをピックアップしてみました。

#### 基本パターン

|種類|抽象メソッド|概要|
|:---|:---|:---|:---|
|**Consumer\<T>**|void accept(T t)|T型の引数を受け取り、戻り値を返さない|
|**Supplier\<T>**|T get()|引数がなく、T型の戻り値を返す|
|**Function\<T,R>**|R apply(T t)|T型の引数を受け取り、R型の戻り値を返す|
|**UnaryOperator\<T>**|T apply(T t) |T型の引数を受け取り、同じT型の戻り値を返す|
|**Predicate\<T>**|boolean test(T t)|T型の引数を受け取り、booleanの値を結果として返す|

※受け取れる引数はどれも1つです

仮に

- 文字列を渡して
- その文字列をただ表示させる

といった処理をしたい場合はどれになるでしょうか。

処理の内容としては
引数としてString型の文字列を渡してSystem.out.println()で表示させるだけなので、
**引数が1つ、戻り値がないパターン**

答えは**`Consumer<String>`**になりますね。


また、**引数を2つ渡したい場合**もあるかと思います。
その時は先頭に*<b>Bi<b>*を付けるというパターンになっています。
※UnaryOperatorは先頭に*<b>Binary<b>*に変わりBinaryOperatorとなります。
#### 引数が2つのパターン

|種類|抽象メソッド|概要|
|:---|:---|:---|:---|
|**BiConsumer\<T,U>**|void accept(T t, U u)|T型とU型の引数を受け取り、戻り値を返さない|
|**BiFunction\<T,U,R>**|R apply(T t, U u)|T型とU型の引数を受け取り、R型の戻り値を返す|
|**BinaryOperator\<T>**|T apply(T t, T t) |T型の引数を2つ受け取り、同じT型の戻り値を返す|
|**BiPredicate\<T,U>**|boolean test(T t, U u)|T型の引数を受け取り、booleanの値を結果として返す|



他にも、
「引数としてintを受け取り、R型の戻り値を返す」**`IntFunction<R>`**
「T型の引数を受け取りintの戻り値を返す」**`ToIntFunction<T>`**

といった、プリミティブ型を扱える関数型インターフェース等も多数用意されていますが、基本的には最初の5つをしっかり覚えることが大事かと思います！

#関数型インターフェースのまとめ

* 関数型インターフェースとは抽象メソッドが一つしか定義されていないインターフェースのこと
* 関数型インターフェースは自分で定義する前に既に用意されているもの**`(java.util.function)`**が利用できないのかを考えてみる
* 関数型インターフェースの実装はラムダ式を用いると簡単に行える  ※ 第三回を見て下さい

#おわりに
Stream APIの為の事前知識ということで、全体的に深いところは突っ込まないようにして書いてみました。
この投稿だけだといまいちイメージを掴めないところもあるかと思うので、続きの投稿をご覧頂ければと思います！