ラムダ(Java 8+)は簡潔な無名関数であり、関数型インターフェースは正確に1つの抽象メソッドを持つインターフェース — ラムダが実装するターゲット型です。これらの2つが関数型プログラミングをJavaにもたらし、Stream APIを支えています。
なぜ重要なのか
java
{
;
}
1つの抽象メソッドを持つインターフェースは「関数型」です — ラムダはそれを実装できます。ラムダがどのメソッドを表すかについて曖昧性がないためです。
// ❌ verbose anonymous class (pre-Java 8)
Calculator add = new Calculator() {
public int calculate(int a, int b) { return a + b; }
};
// ✅ lambda — same thing, far less boilerplate
Calculator add = (a, b) -> a + b;
Calculator multiply = (a, b) -> a * b;
add.calculate(2, 3); // 5
ラムダ (a, b) -> a + b は単一メソッドの実装を提供します — クラスも、メソッドシグネチャのボイラープレートもありません。
Function<String, Integer> length = s -> s.length(); // takes T, returns R
Predicate<Integer> isPositive = n -> n > 0; // takes T, returns boolean
Consumer<String> printer = s -> System.out.println(s); // takes T, returns void
Supplier<String> greeting = () -> "Hello"; // takes nothing, returns T
BiFunction<Integer, Integer, Integer> sum = (a, b) -> a + b; // two args
Javaは一般的な形式に対して既製の関数型インターフェースを提供しており、標準ライブラリ全体(特にストリーム)で使用されます。
// when a lambda just calls an existing method, use a method reference
list.forEach(System.out::println); // instead of x -> System.out.println(x)
list.stream().map(String::toUpperCase); // instead of s -> s.toUpperCase()
list.stream().map(Integer::parseInt); // static method ref
// passing behavior as arguments — the core enabler of the Stream API
people.stream()
.filter(p -> p.getAge() > 18) // Predicate lambda
.map(Person::getName) // method reference
.forEach(System.out::println); // Consumer
list.sort((a, b) -> a.compareTo(b)); // Comparator lambda
button.addActionListener(e -> handleClick()); // event handler
ラムダと関数型インターフェースは関数型プログラミングをJavaにもたらし、現代的なJavaの書き方を根本的に変えました。
動作をデータとして渡す際の冗長な無名クラスのボイラープレートを排除し、簡潔なイベントハンドラー、コンパレーター、コールバック、そして — 最も重要には — 全体的なStream API(filter/map/reduce操作にラムダを依存)を実現できます。
ラムダが関数型インターフェース(1つの抽象メソッド)、組み込みインターフェース(Function、Predicate、Consumer、Supplier)、メソッド参照を対象としていることを理解することは、慣用的な現代的Javaを書き、ストリーム、コレクション、並行APIを効果的に使用するために不可欠です。
これらは言語の大きな進化を表しており、現在のJavaの流暢さの中心です。