Javaのラムダ式の成り立ち

# Javaラムダ式
## はじめに
サーバサイドエンジニアのliitFeetです。最近はもっぱらkotlinとSpringBootでWebFluxをCoroutineって感じです。
Coroutine難しい・・・

この記事はチームラボエンジニアリングアドベントカレンダー7日目の記事です。

この記事ではJavaのラムダ式の成り立ちについて説明していきます。
え、今更？っていう感じはありますが、goやkotlinなど関数を第一級オブジェクトとして扱えるようになって、実はラムダ式の中身が何かよくわからずに使っていることありそうだなーと思い書きました。

結論から言うと、javaのラムダ式はメソッドが一つしかないインターフェースを実装してインスタンスを作成(new)します。
多分この説明ではなんのこっちゃわからないと思いますので、ある式を計算した結果を表示するプログラムを例に、ラムダ式までの成り立ちを順をおって説明していきます。
1. 通常のクラスを引数で受け渡し
2. インナークラスを引数で受け渡し
3. 無名クラスを引数で受け渡し
4. ラムダ式を引数で受け渡し

ラムダ式は記述量を減らしつつ、人間にもコンパイラーにも読みやすい記述法なんだなーというのを意識するとわかりやすいかなと思います。

また、この記事はJavaのラムダ式の成り立ちについて説明しますが、
ラムダ式自体の詳しい説明(引数が一つだけならカッコは記述不要や関数型インターフェースなど)はしませんのでご了承ください〜。

ソースコードは[GitHub](https://github.com/duosonic62/LambdaExpression)にあります。

## 通常のクラスを引数で受け渡し
まずはノーマルなクラスを受け渡すことで、計算した結果を表示するプログラムを実現したいと思います。
ソースコードは[normalClassパッケージ](https://github.com/duosonic62/LambdaExpression/tree/master/src/normalClass)です。
ソースは下記の4つです。

```
Main.java                   メインのクラス
Display.java                計算結果を表示するクラス
CalcuratorInterface.java    計算について定義したインターフェース
Display.java                CalcuratorInterface

```

まずDisplayクラスでは、計算を担当するcalculatorと計算する値(a, b)を受け取り、
calculatorで(a, b)の値を計算して表示します。

```java
public class Display {
    public void showCalc(CalculatorInterface calculator, int a, int b) {
        System.out.println(calculator.calc(a, b));
    }
}
```

CalculatorInterfaceでは計算を行うメソッド、calcを定義します。

```java

public interface CalculatorInterface {
    int calc(int a, int b);
}
```


AddCalculatorではCalculatorInterfaceを実装して、引数(a, b)を足した値を返します。

```java
public class AddCalculator implements CalculatorInterface {

    @Override
    public int calc(int a, int b) {
        return a + b;
    }
}

```

MainクラスではDisplayクラスに計算を行う```calculator```と計算する値を渡して計算結果を表示させています。

```java
public class Main {
    public static void main(String[] args) {
        Display display = new Display();
        CalculatorInterface calculator = new AddCalculator();
        display.showCalc(calculator, 3, 5);
    }
}

```

特に問題ないかと思います。

## インナークラスで受け渡し
次に、インナークラスを使ってみたいと思います。
ソースコードは[innerClass](https://github.com/duosonic62/LambdaExpression/tree/master/src/innerClass)パッケージです。
CalcuratorInterface.javaと、Display.javaは変更ありません。
  
前述の通常のクラスを使う場合、ただMainクラスだけで使う足し算をするだけのために、わざわざクラスファイルを作成するのは面倒だと思いませんか。
そこで、以下のようにMainの中でAddCalculatorを定義してしまい、AddCalculator.javaを無くすことができます。

```java
public class Main {
    public static void main(String[] args) {
        Display display = new Display();

        // インナークラスを定義
        class AddCalculator implements CalculatorInterface {

            @Override
            public int calc(int a, int b) {
                return a + b;
            }
        }

        CalculatorInterface calculator = new AddCalculator();
        display.showCalc(calculator, 3, 5);
    }
}
```

## 無名クラスを関数で受け渡し
次に無名クラスを使っていきます。
ソースコードは[anonymousInnerClassパッケージ](https://github.com/duosonic62/LambdaExpression/tree/master/src/anonymousInnerClass)です。
インナークラスを使って、クラスファイルを一つ作らずにすみましたが、ここで一回しか使わないであれば、このクラスに名前つける必要はありません。
そこでcalculatorに代入するタイミングで、その場でクラスを実装してしまいましょう。

```java
public class Main {
    public static void main(String[] args) {
        Display display = new Display();

        // 一回なら使い捨てで
        CalculatorInterface calculator = new CalculatorInterface() {
            @Override
            public int calc(int a, int b) {
                return a + b;
            }
        };
        display.showCalc(calculator, 3, 5);
    }
}
```

これが無名クラスです。使い回しはできませんが、記述量がだんだん減ってきましたね。


## ラムダ式
最後に、ラムダ式を使った引数の受け渡しです。
ソースコードは[lambdaExpressionパッケージ](https://github.com/duosonic62/LambdaExpression/tree/master/src/lambdaExpression)です。
無名クラスを使うことでクラスの名前を省略し、記述量を抑えることができました。
ですが、CalculatorInterfaceを実装するなら```new CalculatorInterface()```と書かなくともコンパイラでよしなにしてくれそうです。 
それに、メソッドが一つしかないなら、どのメソッドを実装するかも一意に決まっています。  
メソッドの引数もインターフェースで型の定義がされているし、宣言しなくともコンパイラは型を推測することが可能です。

ラムダ式では、実装に必要なメソッドのみ記述することでインスタンスを生成することができます。

```java
public class Main {
    public static void main(String[] args) {
        Display display = new Display();

        //　実装するメソッドの処理のみかく
        CalculatorInterface calculator = (a, b) -> {
            return a + b;
        };

        display.showCalc(calculator, 3, 5);
    }
}
```

ラムダ式は以下の部分ですね。この部分で、CalculatorInterfaceのcalcメソッドの実装を表現しています。

```
(a, b) -> {
    return a + b;
};
```
```(a, b)```は引数です。メソッドの引数はインターフェースで型の定義がされているのでわざわざ書きません。
```->```以降がメソッドの実装です。```a+b```をリターンしてますね。

## まとめ
いかがでしたでしょうか。
最初に「javaのラムダ式はメソッドが一つしかないインターフェースを実装してインスタンスを作成(new)します。」と書いた意味が伝わったら幸いです！

完全に余談ですが、kotlinとかみたいに第一級オブジェクトとして関数扱えると楽でいいですよね・・・引数もわかりやすい・・・

```kotlin
val calculator = {a: Int, b: Int -> a + b}
showCalc(calculator, 3, 5)
```

```kotlin
fun showCalc(calc: (Int, Int) -> Int, a: Int, b: Int) {
  println(calc.invoke(a, b))
}
```