Javaのラムダ式ってなんだ?
皆さん、こんにちは。LP開発グループのn-ozawanです。
クリスマスですね。トナカイの鼻が赤いのは、非常に多くの毛細血管が集まっており、脳の温度調節をしているそうです。
本題です。
JavaのStream APIなどでよく見かける() -> {...}のような書き方、そういう物だと思って良く理解せずに使ってませんか?これはJava8で導入されたラムダ式と呼ばれる書き方で、今回はこのラムダ式がどういうものかを解説します。
目次
ラムダ式
ラムダ式とは?
ラムダ式は、関数型インターフェースの抽象メソッドを匿名で実装するための記法です。難しいですね。順を追って説明します。
Javaはオブジェクト指向言語であり、原則、クラスを実装する必要があります。ラムダ式が登場する以前は、メソッド1つ定義するにしても、必ずクラスを定義していました。以下は無名クラスを使って定義したコードです。実装も面倒ですし、何よりも可読性が非常に悪いです。
Runnable runner = new Runnable() {
@Override
public void run() {
System.out.println("Hello, World!");
}
};
runner.run();そこで登場したのがラムダ式でした。ラムダ式であれば以下のような記述ができます。
Runnable runner = () -> System.out.println("Hello, World!");
runner.run();これは、これまで記述していたクラスを簡略化したものになります。処理内部ではクラスが存在することに注意が必要です。
関数型インターフェースとは?
関数型インターフェースとは、抽象メソッドを1つだけ持つインターフェースのことです。ラムダ式は、抽象メソッドを1つだけ持つインターフェースでのみ、簡略化した記述ができるようになります。
interface FunctionalInterfaceExample {
void run(String message);
}
FunctionalInterfaceExample example = (message) -> System.out.println(message);
example.run("Hello, Functional Interface!");もし、抽象メソッドが2つ以上ある場合、ビルド時にエラーになります。
interface FunctionalInterfaceExample {
void run(String message);
void hoge();
}
FunctionalInterfaceExample example = (message) -> System.out.println(message);
example.run("Hello, Functional Interface!");
// main.java:24: error: incompatible types: FunctionalInterfaceExample is not a functional interface
// FunctionalInterfaceExample example = (message) -> {
// ^
// multiple non-overriding abstract methods found in interface FunctionalInterfaceExample
// 1 error
// error: compilation failed関数型インターフェースを定義する場合は、@FunctionalInterfaceあのテーションを付けて、明示的に関数型インターフェースであることを示すのが一般的です。
@FunctionalInterface
interface FunctionalInterfaceExample {
void run(String message);
}標準の関数型インターフェース
いちいち関数型インターフェースを定義するまでもない、その場限りの処理をラムダ式で記述したい場合があります。Javaは、汎用的に使える関数型インターフェースを用意しています。
Function<T,R>
Function<T,R>は、1つの引数と返却値を持つ関数型インターフェースです。一般的に受け取った値を変換して返す処理などに使われます。
Function<String, Integer> stringLengthFunction = (str) -> str.length() * 10;
int length = stringLengthFunction.apply("Hello");
System.out.println("Length: " + length); // Outputs: Length: 50Consumer<T>
Consumer<T>は、1つの引数を受け取り、返却値を持たない関数型インターフェースです。一般的に受け取った値の処理などに使われます。
Consumer<String> printer = (message) -> System.out.println(message);
printer.accept("Hello, Consumer!"); // Outputs: Hello, Consumer!Predicate<T>
Predicate<T>は、1の引数を受け取り、booleanを返却する関数型インターフェースです。一般的に受け取った値に対して判定を行う処理などに使われます。
Predicate<String> isHello = (str) -> str != null && str.equals("hello");
System.out.println(isHello.test("hello")); // Outputs: true
System.out.println(isHello.test(null)); // Outputs: falseSupplier<T>
Supplier<T>は、引数なしで、何かしらの結果を返却する関数型インターフェースです。一般的に乱数値の生成や初期化処理などに使われます。
Supplier<Double> randomSupplier = () -> Math.random();
System.out.println("Random number: " + randomSupplier.get());まとめ
ラムダ式は、関数型インターフェースを簡潔に実装し、振る舞いを値として扱うための強力な機能です。ラムダ式を使いこなすことで、コードがシンプルになり、保守性も向上します。
ではまた。
