苦手なラムダ式を克服する

## はじめに
今までラムダ式は**コードを簡潔に書ける便利なもの**くらいの理解でしかなかったので、今回はラムダ式を分解しながら克服していこうと思います。

## ラムダ式とは
> ラムダ式（lambda expression）とは、プログラミング言語に用意された記法の一つで、名前の無い無名関数（匿名関数）を簡潔に定義するもの。

よくわかりません。実際にコードで理解していきます。

## 前提知識
ラムダ式は**ローカルクラス**と**無名クラス**という仕組みを利用しています。

### ローカルクラスとは
ローカルクラスは**メソッド内で宣言するクラス**のことです。
インターフェースを実装したローカルクラスも定義できます。
```java
public static void main(String[] args) {

  class Local implements Runnable {
    @Override
    public void run() {
      System.out.println("Hello Lambda!");
    }
  }

  Runnable runner = new Local();
  runner.run(); // Hello Lambda!
}
```

### 無名（匿名）クラスとは
無名クラスは、**インターフェースや抽象クラスのインスタンスを直接生成するためのクラス**です。
```java
public static void main(String[] args) {

  Runnable runner = new Runnable() {
    @Override
    public void run() {
      System.out.println("Hello Lambda!");
    }
  };

  runner.run(); //Hello Lambda!
}
```

### ラムダ式とは
無名クラスから更に「new Runnable(){}」と「public void run」を省略してラムダ式となります。
```java
public static void main(String[] args) {

  Runnable runner = () -> { System.out.println("Hello Lambda!"); };
  runner.run(); //Hello Lambda!
}
```

最初の()はrunメソッドの引数を表し、->{}の中身はrunメソッドの実装内容になります。
runner変数にはRunnableを実装した無名クラスのインスタンスが代入されます。
つまり、ラムダ式とはインターフェースを実装したインスタンスを生成する式といえます。

ところで「new Runnable(){}」を省略したら、何型のインスタンスを生成するのかわかりません。
Javaでは代入される変数の型によって自動的に推論する仕組みになっています。
この仕組みを**型推論**と呼びます。

また、「public void run」を省略すると、複数メソッドが定義されているインターフェースの場合、どのメソッドをオーバーライドするのかわかりません。
そのため、**ラムダ式で使用できるのは抽象メソッドが一つのインターフェースのみ**となります。

Rannableインターフェースだけでは引数なし、戻り値なしのラムダ式しか作れません。
他の形で作成したい場合は、**関数型インターフェース**を利用します。

## 関数型インターフェースとは
関数型インターフェースとは、**抽象メソッドが一つだけあるインターフェース**のことです。
default, static, privateメソッドは含まれていても問題ありません。
例えば以下のようなインターフェースがあります。

### Function< T,R >
Functionは、値を変換する為の関数型インターフェース。
Function< T,R >のTはメソッドの引数の型、Rは戻り値の型を指定します。
引数を受け取り、変換（演算）して別の値を返す。
メソッドは R apply(T) です。
```java
Function<Integer, String> asterisker = (i) -> { return "*"+ i; };
String result = asterisker.apply(10);
System.out.println(result); // *10
```

### Consumer< T >
Consumerは、引数を受け取り、それを使って処理を行う為の関数型インターフェース。
Consumer<T>のTはメソッドの引数の型を指定します。
値を返さないので、基本的に副作用を起こす目的で使用する。
メソッドは void accept(T) です。
```java
Consumer<String> buyer = (goods) -> { System.out.println(goods + "を購入しました"); };
buyer.accept("パソコン"); // パソコンを購入しました。
```

### Predicate< T >
Predicateは判定を行う為の関数型インターフェース。
Predicate<T>のTはメソッドの引数の型を指定します。
引数を受け取り、判定を行い、真偽値（判定結果）を返す。
メソッドは boolean test(T) です。
```java
Predicate<String> checker = (s)-> { return s.equals("Java"); };
boolean result = checker.test("Java");
System.out.println(result); //true
```

## ラムダ式のメリット

次のコードはラムダ式で書かれたものです。
```java
List<Integer> numbers = List.of(3, 1, -4, 1, -5, 9, -2, 6, 5, 3, 5);
numbers.stream()
        .filter(number -> Math.abs(number) >= 5)
        .forEach(System.out::println);
```

出力結果は以下
```
-5
9
6
5
5
```

非常にシンプルです。
ラムダ式を利用しない場合は以下のようになります。
```java
List<Integer> numbers = List.of(3, 1, -4, 1, -5, 9, -2, 6, 5, 3, 5);

numbers.stream()
        .filter(new Predicate<Integer>() {
            @Override
            public boolean test(Integer number) {
                return Math.abs(number) >= 5;
            }
        })
        .forEach(new Consumer<Integer>() {
            @Override
            public void accept(Integer number) {
                System.out.println(number);
            }
        });
```
コードの記述量が増え、見づらくなりました。
ラムダ式を用いれば、**クラスの定義とインスタンスの生成を合わせて行うことができますので、コードの記述がシンプルになるメリットがあり、簡潔でわかりやすいコードを書くことができます。**

## まとめ
今回記事作成を通してラムダ式を克服できた気がします笑
ざっくり説明しましたが、より詳しいラムダ式文法など気になる方はぜひ調べてみてください！