【Java】ラムダ式を何となく理解する

#ラムダ式を理解するための前置き
##関数オブジェクト
JavaSE7まででは、変数に代入できるのは第一級オブジェクト（データ、インスタンスなど）のみであるとされていたが、
JavaSE8以降では、***関数***も変数に代入できるようになった。

関数にとって、名前はあってもなくても良いものである。
入力（引数）と出力（返り値）さえあれば関数の役割は果たせる。

例えば…
＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝
職場の上司に、
「この手順書に沿って折り紙折っといて～、午前中に１００個作っておくように！」
と指示され、手順書と折り紙１００枚を渡された（どんな仕事やねん）

しかし、手順書には折り方が載ってあるだけで、
何が出来上がるのかとか、手順書の名前なんてものはない。
それでも、手順に沿って折り紙をしていった結果、
なにか良くわからないけど１００個分完成した
＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝

…みたいな感じである。

プログラミングにおける関数も同様で、
「名前なんか無くても入力・出力さえあれば関数でいいよね～」
って考えでJavaSE8からは変数に関数が代入できるようになった。

変数に代入される関数のことを、***関数オブジェクト***といったりもする。

##変数への関数オブジェクト代入の例
```java
import java.util.function.*;

public class Sample {
	private static int sum(int x, int y) {
		return x + y;
	}
	
	public static void main(String[] args) {
		IntBinaryOperator func = Sample::sum;
		int x = func.applyAsInt(10, 20);
		System.out.println(x);//result: 30
	}
}
```
ざっくり説明すると、Sampleクラスにあるstaticなsumメソッドへの参照を、IntBinaryOperatorインターフェース）のfuncに代入することで、
IntBinaryOperatorインターフェースで予め定義されているapplyAsIntメソッドはsumメソッドの機能としてオーバーライドされ、
func.applyAsIntはsumの機能を提供するようになった、という感じ。

なお、今回のように関数オブジェクトを変数に代入できるのには条件がある。
＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝
**１．変数（左側）の条件…抽象メソッドを１つしか有していない関数型インターフェースである(*1)こと**

***２．関数オブジェクト（右側）の条件…関数型インターフェースのメソッドと関数オブジェクトの「戻り値の型・引数の数・引数の型」が一致していること***

(*1)SAMインターフェース(Single Abstract Method Interface)という
＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝

実際、↑のコードでも条件は満たす。

`IntBinaryOperator`というのをリファレンスで調べてみると、

* これは関数型インタフェースなので、ラムダ式またはメソッド参照の代入先として使用できます。
* `int applyAsInt​(int left, int right)`　これ１つのみメソッドをもつ

との記載がされている。
今回の関数オブジェクトint sum(int x, int y)と比較をしても条件は満たしている。

IntBinaryOperatorの他にも関数型インターフェースは以下のようなものがある

| 関数型インターフェース | メソッド |
|:-:|:-:|
| int IntToDoubleFunction(int, int)  | applyAsDouble  |
| void IntConsumer(int)  | accept  |
| int IntSupplier() | getAsInt  |
| boolean IntPredicate(boolean)  | test  |


#ラムダ式
ラムダ式とは、関数を必要なタイミングで即座に利用できるようにするための構文のこと
先ほどの関数オブジェクト代入コードを、ラムダ式を用いて表すと以下のようになる

```java
import java.util.function.*;

public class Sample2 {
	public static void main(String[] args) {
		IntBinaryOperator func = (int x, int y) -> {
			return x + y;
		};
		int x = func.applyAsInt(10, 20);
		System.out.println(x);// result: 30
	}
}
```
非常にシンプルなコードになったが、実態は先ほどと変わらず、
引数x, yの合計値を戻り値として返す関数への参照を、IntBinaryOperatorインターフェースのfunc変数に代入している。

また、以下のように、条件を満たせばもっと簡略化することも可能である。

```java
import java.util.function.*;

public class Sample3 {
	public static void main(String[] args) {
		IntBinaryOperator func = (x, y) -> x + y;
		int x = func.applyAsInt(10, 20);
		System.out.println(x);// result: 30
	}
}

```

##ラムダ式の実践的な使い方の具体例
###StreamAPI
***void forEach(Consumer<T>)***
関数型インターフェースConsumerの変数を引数に取り、要素の数だけ処理を繰り返す

```java
import java.util.ArrayList;
import java.util.List;

public class RamdaSample2 {
	public static void main(String[] args) {
		int[] nums = { 9, -3, 1, 0, 2 };
		List<Integer> numsList = new ArrayList<>();
		for (int n : nums)
			numsList.add(n);
		
		numsList.stream().forEach((i) -> {
			System.out.println(i);
		});
	}
}
//9
//-3
//1
//0
//2
```

ちなみに、よく使う関数型インターフェースには以下のようなものがある
（T:引数, R：戻り値）

| 関数型インターフェース | メソッド |
|:-:|:-:|
| Functions＜T, R＞  | R apply(T)  |
| Consumer＜T＞  | void accept(T)  |
| Supplier | T get()  |
| Predicate＜T＞  | boolean test(T)  |

#おわりに
関数オブジェクトの項目だけでも読んでおけば、
ラムダ式がなぜあんな意味不明な構造になっているかのかは何となくわかるはず