【Java】ラムダ式周りについて解説

## 初めに
Javaでは関数インタフェースがあるが、ラムダ式を使うとかなり簡単に処理を書くことができる。ラムダ式ではいくつかルールがある。またラムダ式や関数インタフェースの変換についても理解しておこう。

## ラムダ式で使う変数は実質finalでないとエラー
```ruby:Java(Main.java)
import java.util.function.Supplier;

public class Main {
	public static void main(String[] args) {
		int i = 0;
		Supplier<Integer> foo = () -> i;
		i++;
		
		System.out.println(foo.get());
	}
}
```

結果
```
Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
	Local variable i defined in an enclosing scope must be final or effectively final

	at Main.main(Main.java:6)
```
ラムダ式を囲むブロック(今回はmainクラス)の中で宣言されたローカル変数をラムダ式内で利用する場合、その変数が**finalで宣言**されているか、**実質final**でなければいけない。そのため変数iをインクリメントするとエラーになる。
以下のようにi++をコメントアウトするとエラーは発生しない。

```ruby:Java(Main.java)
import java.util.function.Supplier;

public class Main {
	public static void main(String[] args) {
		int i = 0;
		Supplier<Integer> foo = () -> i;
//		i++;
		
		System.out.println(foo.get());
	}
}
```
結果
```
0
```

## ラムダ式で使う変数はローカル変数とかぶるとエラー
以下のようにラムダ式内で使う変数numとローカル変数numは変数名がかぶっているのでエラー
```ruby:Java(Main.java)
import java.util.function.Consumer;

public class Main {
	public static void main(String[] args) {
		int num = 1;
		Consumer<Integer> count = (num) -> {
				System.out.println(num + 1);
		};
		count.accept(10);
	}
}
```


    

## 関数インタフェースからラムダ式への変換
Functionインタフェースと匿名クラスを使用してソースを記述すると以下のようになる。
```ruby:Java(Main.java)
import java.util.function.Function;

public class Main {
	public static void main(String[] args) {
		String str = new Function<String,String>(){
			public String apply(String str) {
				return "Hello" + str;
			}
		}.apply("suzuki");
		System.out.println(str);
	}
}
```

結果
```
Hellosuzuki
```

しかしこれだとかなり長くて冗長である。これをさらに短い形にしたのがラムダ式である。

```ruby:Java(Main.java)
import java.util.function.Function;

public class Main {
	public static void main(String[] args) {
		Function<String,String> hello = (String str) -> {
			return "Hello" + str;
		};
		System.out.println(hello.apply("suzuki"));
	}
}
```
ここからさらに省略することもできる。以下の条件に当てはまるか確認しよう。

①引数の型が推論できるか
Function<String,String>で明記しているため、strはString型だと推論できる。その場合以下のように引数の型を省略できる。
```ruby:Java(Main.java)
 import java.util.function.Function;

public class Main {
	public static void main(String[] args) {
		Function<String,String> hello = (str) -> {
			return "Hello" + str;
		};
		System.out.println(hello.apply("suzuki"));
	}
}
```
②引数が1つかどうか
引数が1つの場合、引数の()を省略できる。

```ruby:Java(Main.java)
 import java.util.function.Function;

public class Main {
	public static void main(String[] args) {
		Function<String,String> hello = str -> {
			return "Hello" + str;
		};
		System.out.println(hello.apply("suzuki"));
	}
}
```

③処理が1文かどうか
処理が1文の場合、処理文の{}を省略できる。さらにreturnも省略可能。

```ruby:Java(Main.java)
 import java.util.function.Function;

public class Main {
	public static void main(String[] args) {
		Function<String,String> hello = (str) -> "Hello" + str;
		System.out.println(hello.apply("suzuki"));
	}
}
```

## メソッド参照
上記のラムダ式をメソッド参照で呼び出す場合は**クラス名::メソッド名**または**参照型変数::メソッド名**でy日だし、関数インタフェースに代入することが可能。イメージとしてはメソッド自体を変数に入れて、それを使う感じ。またメソッドの後ろに()はつけないので注意。

```ruby:Java(Main.java)
import java.util.function.Function;

public class Main {
	public static void main(String[] args) {
		Function<String,String> hello = Main::sayHello;

		System.out.println(hello.apply("suzuki"));
	}
	public static String sayHello(String str) {
		return "Hello" + str;
	}
}
```

## (復習)例を用いて関数インタフェースからラムダ式、メソッド参照に置き変える
①関数インタフェース
```ruby:Java(Main.java)
import java.util.List;
import java.util.function.Consumer;

public class Main {
	public static void main(String[] args) {
		List<Integer> list = List.of(1,2,3);
		Consumer<Integer> print = new Consumer<Integer>() {
			public void accept(Integer x) {
				System.out.println(x);
			}
		};
		list.forEach(print);
	}
}
```

②ラムダ式
```ruby:Java(Main.java)
import java.util.List;

public class Main {
	public static void main(String[] args) {
		List<Integer> list = List.of(1,2,3);
		list.forEach((x) -> System.out.println(x));
	}
}
```

③メソッド参照
```ruby:Java(Main.java)
import java.util.List;
import java.util.function.Consumer;

public class Main {
	public static void main(String[] args) {
		List<Integer> list = List.of(1,2,3);
		Consumer<Integer> print = System.out::println;
		list.forEach(print);
	}
}
```

## まとめ
* ラムダ式で使う変数は実質final
* ラムダ式で使う変数はローカル変数とかぶらないようにする
* 関数インタフェースからラムダ式、メソッド参照へ変換可能
* ラムダ式は条件に合致すれば省略できる部分がある
* メソッド参照はメソッド名の後ろに()をつけない