【Java入門】ラムダ式について

#目的

Java言語を含めたプログラミングの学習を始めたばかりの方、既学習者の方は復習用に、
今回は**ラムダ式**について学ぶために書いています。

[【Java入門目次】](https://qiita.com/morioheisei/items/6c7fc063c26e63d890ac)
・[変数と型](https://qiita.com/morioheisei/items/3d27e82d6cd88068dded)
・[型変換](https://qiita.com/morioheisei/items/28ab5fd69570e01cd317) 
・[変数のスコープ](https://qiita.com/morioheisei/items/c56ab32b68ce92d32206)
・[文字列の操作](https://qiita.com/morioheisei/items/e58adadd8535971b69be)
・[配列の操作](https://qiita.com/morioheisei/items/b6acbb1b0c07b40e7f56)
・[演算子](https://qiita.com/morioheisei/items/63088c61e8234dc1f7f5)
・[条件分岐](https://qiita.com/morioheisei/items/194cdc73a29bddade0a4)
・[繰り返し処理](https://qiita.com/morioheisei/items/e686e33b60c6a60e87f0)
・クラスについて（準備中）
・抽象クラス（準備中）
・インターフェース（準備中）
・カプセル化（準備中）
・モジュールについて（準備中）
・[例外処理について](https://qiita.com/morioheisei/items/d25d2b67e7530c1aab9d)
・ラムダ式について ←今ここ
・[Stream APIについて](https://qiita.com/morioheisei/items/3a7be38b4d3ef712ccb2)

#ラムダ式とは

関数型インターフェースの実装は、無名クラス（匿名クラス）を利用する事で行っていたが、
ソースコードの可読性が良くなく、冗長になりがちであった。

そのような悩みを解決するのが**ラムダ式**。
Java8から導入された構文であり、無名クラス（匿名クラス）を利用する事と同様の実装が、シンプルかつ可読性の高い状態で可能になった。

**ラムダ式の構文**
`( 実装するメソッドの引数 ) -> { 処理 };`

#ラムダ式の例

実際にどの様にシンプルかつ可読性が高くなったのか、簡単な例で比較してみます。

まずは、無名クラス（匿名クラス）での実装。

```java:無名クラス（匿名クラス）
// 自作関数型インターフェース
interface Greet {
  String sayHello(String name);
}

public class Lambda {
  public static void main(String[] args) {

    // Greetインターフェースの実装を行った、無名クラスのインスタンスを生成
    Greet case1 = new Greet(){

      // 抽象メソッドsayHelloをオーバーライド
      public String sayHello(String name) {
        return name + " さん、こんにちは！";
      }

    };

    // 実装したsayHelloメソッドに文字列を渡す
    String tanaka = case1.sayHello("tanaka");
    System.out.println(tanaka); // tanaka さん、こんにちは！ と出力される

  }
}
```

次に、`ラムダ式`で同様の処理を実装します。

```java:ラムダ式
// 自作関数型インターフェース
interface Greet {
  String sayHello(String name);
}

public class Lambda {
  public static void main(String[] args) {

    // ラムダ式でGreetインターフェースを実装し、インスタンスを生成
    Greet case2 = (String name) -> { return name + " さん、こんにちは！"; };

    // 実装したsayHelloメソッドに文字列を渡す
    String suzuki = case2.sayHello("suzuki");
    System.out.println(suzuki); // suzuki さん、こんにちは！ と出力される
  }
}
```

new Greet() と記述、その中の抽象メソッドのメソッド名を再度記述する事なくオーバーライドする事ができています。
これは、`関数型インターフェースは抽象メソッドが1つしかない為`、**ラムダ式ではどのメソッドを実装するのか判断できる**様になっているからです。



##ラムダ式の省略した記述方法

また特定の場合に限り、更に省略して記述する事も可能です。


###1.型推論による型の省略

```java:before
Greet case2 = (String name) -> { return name + " さん、こんにちは！"; };
```

```java:after
Greet case2 = (name) -> { return name + " さん、こんにちは！"; };
```

関数型インターフェースの宣言時にメソッドの引数の型は決定している為、`実装時に引数の型を明記せず`に省略が可能です。

###2.()の省略

```java:before
Greet case2 = (String name) -> { return name + " さん、こんにちは！"; };
```

```java:after
Greet case2 = name -> { return name + " さん、こんにちは！"; };
```

引数が1つの場合、`()`の省略が可能です。ただし、引数がない場合、複数ある場合、引数が１つであっても型を明記した場合は省略できません。

###3.{}の省略

```java:before
Greet case2 = (String name) -> { return name + " さん、こんにちは！"; };
```

```java:after
Greet case2 = name -> name + " さん、こんにちは！";
```

処理が1文の場合、`{}`の省略が可能です。また、`{}`を省略している場合、`return`も省略可能です。

シンプルに書けますね！

#関数型インターフェースの紹介

先程は自作関数型インターフェースを用いた例でしたが、
java.util.functionパッケージとして提供されている関数型インターフェースの例もいくつか紹介していきます。

###`1.Function<T, R>`

Tの型の引数を受け取って、Rの型の値を返します。
メソッドはR apply(T t)です。

```java:Functionの例
Function<Integer, String> funcTest = number -> number + "です";
String resutlt = funcTest.apply(19);
System.out.println(resutlt); // 19です
```

###`2.Consumer<T>`

Tの型の引数を受け取ります。
メソッドは、void accept(T t)です。

```java:Consumerの例
Consumer<String> weather = str -> System.out.println("今日の天気は" + str);
weather.accept("雨です"); // 今日の天気は雨です
```

###`3.Predicate<T>`

Tの型の引数を受け取ります。
メソッドは、boolean test(T t)です。

```java:Predicateの例
Predicate<String> check = str -> 5 < str.length();
boolean result = check.test("みたらし団子");
System.out.println(result); // true

boolean result2 = check.test("豆大福");
System.out.println(result2); // false
```

###`4.Supplier<T>`

引数では何も受け取りません。
メソッドは、T get()です。

```java:Supplierの例
Supplier<String> name = () -> "鈴木一郎";
System.out.println(name.get()); // 鈴木一郎
```

###`5.UnaryOperator<T>`

Functionを拡張したインターフェースです。引数で受け取る型と返す型の両方が一緒です。
メソッドは、T apply(T t)です。

```java:UnaryOperatorの例
UnaryOperator<String> breakfast = food -> "朝はやっぱり" + food;
System.out.println(breakfast.apply("カレー")); // 朝はやっぱりカレー
```

#その他のメソッドでラムダ式を使う

コレクションフレームワークを中心として提供されているメソッドをラムダ式を用いて実装する事も可能です。（複数のオブジェクトの格納、取り出し、削除が行える）
なぜなら、メソッドの引数に関数型インターフェースを受け取っているからです。こちらもいくつか紹介します。


###`1.default boolean removeIf(Predicate<? super E> filter)`

指定された処理を満たすコレクション要素を全て削除するメソッドです。

```java:removeIfメソッド
List<Integer> numbers = new ArrayList<>(Arrays.asList(1,2,3,4,5,6));
numbers.removeIf(number -> number % 2 == 0); // 2で割って、0になるものを削除する
System.out.println(numbers); // [1, 3, 5]
```

###`2.default void replaceAll(UnaryOperator<E> operator)`

指定された処理を行い、リストの要素を置き換えるメソッドです。

```java:replaceAllメソッド
List<String> names = Arrays.asList("tanaka", "suzuki", "yamada");
names.replaceAll(name -> name.toUpperCase()); // 全て大文字に置き換える
System.out.println(names); // [TANAKA, SUZUKI, YAMADA]
```

###`3.default void sort(Comparator<? super E> c)`

指定した順序にしたがってリストをソートするメソッドです。

```java:sortメソッド
List<Integer> randomNum = Arrays.asList(30, 50, 10, 20, 40);
randomNum.sort((a, b) -> b.compareTo(a)); // 降順に並び替え
System.out.println(randomNum); // [50, 40, 30, 20, 10]

randomNum.sort((Integer a, Integer b) -> { return a.compareTo(b); } ); // 昇順に並び替え
System.out.println(randomNum); // [10, 20, 30, 40, 50]
```

###`4.void forEach(Consumer<? super T> action)`

全ての要素に対して指定されたアクションを実行するメソッドです。

```java:forEachメソッド
List<String> names = Arrays.asList("tanaka", "suzuki", "yamada");

// 名前+半角スペース区切り で1件ずつ出力する様にしている
names.forEach(name -> System.out.print(name + " ")); // tanaka suzuki yamada
```

#終わりに

ラムダ式という構文について触れてみました。
ソースコードが短くなり、実装部分だけの記述で済むのは便利ですね。

Stream APIについては、別記事にまとめようと思います。

#参考サイト

**[java.util.function](https://docs.oracle.com/javase/jp/8/docs/api/java/util/function/package-summary.html)**
**[【初心者向け】今更聞けない？Java8のラムダ式について知ろう！](https://www.casleyconsulting.co.jp/blog/engineer/114/)**