初めに
Javaでは関数インタフェースがあるが、ラムダ式を使うとかなり簡単に処理を書くことができる。ラムダ式ではいくつかルールがある。またラムダ式や関数インタフェースの変換についても理解しておこう。
ラムダ式で使う変数は実質finalでないとエラー
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++をコメントアウトするとエラーは発生しない。
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は変数名がかぶっているのでエラー
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インタフェースと匿名クラスを使用してソースを記述すると以下のようになる。
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
しかしこれだとかなり長くて冗長である。これをさらに短い形にしたのがラムダ式である。
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で明記しているため、strはString型だと推論できる。その場合以下のように引数の型を省略できる。
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つの場合、引数の()を省略できる。
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も省略可能。
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日だし、関数インタフェースに代入することが可能。イメージとしてはメソッド自体を変数に入れて、それを使う感じ。またメソッドの後ろに()はつけないので注意。
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;
}
}
(復習)例を用いて関数インタフェースからラムダ式、メソッド参照に置き変える
①関数インタフェース
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);
}
}
②ラムダ式
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));
}
}
③メソッド参照
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
- ラムダ式で使う変数はローカル変数とかぶらないようにする
- 関数インタフェースからラムダ式、メソッド参照へ変換可能
- ラムダ式は条件に合致すれば省略できる部分がある
- メソッド参照はメソッド名の後ろに()をつけない