
はじめに
Javaは、もともとオブジェクト指向プログラミング言語として設計されましたが、Java 8の登場により関数型プログラミングの要素が加わり、より柔軟で表現力のあるコードを書けるようになりました。その中でも重要な要素の一つが関数型インターフェイスです。
本記事では、関数型インターフェイスについて詳しく解説し、ラムダ式やメソッド参照を用いた表現方法、さらにStream APIでの活用方法を紹介します。
関数型インターフェイスとは?
基本的な概要
関数型インターフェイスとは、抽象メソッドを1つだけ持つインターフェイスのことです。@FunctionalInterfaceアノテーションを使用することで、意図的に関数型インターフェイスとして定義でき、複数の抽象メソッドを持たないことをコンパイラが保証してくれます。
代表的な関数型インターフェイス
Javaには標準で以下のような関数型インターフェイスが用意されています。

以下でサンプルコードを見ながら、それぞれのインターフェースに関して解説します。
Consumer<T>
以下は、文字列を出力する例です。accept() メソッドで処理を実行します。
import java.util.function.Consumer;
public class ConsumerExample {
public static void main(String[] args) {
Consumer<String> printer = message -> System.out.println(message);
printer.accept("Hello, Consumer!"); // 出力: Hello, Consumer!
}
}
Predicate<T>
以下は、整数が偶数かどうかを判定する例です。test() メソッドで条件を評価します。
import java.util.function.Predicate;
public class PredicateExample {
public static void main(String[] args) {
Predicate<Integer> isEven = number -> number % 2 == 0;
System.out.println(isEven.test(4)); // 出力: true
System.out.println(isEven.test(5)); // 出力: false
}
}
Function<T, R>
以下は、文字列の長さを返す例です。apply() メソッドで処理を実行します。
import java.util.function.Function;
public class FunctionExample {
public static void main(String[] args) {
Function<String, Integer> stringLength = str -> str.length();
System.out.println(stringLength.apply("Java")); // 出力: 4
System.out.println(stringLength.apply("Hello, Function!")); // 出力: 17
}
}
Supplier<T>
以下は、現在の日付を返す例です。get() メソッドで結果を取得します。
import java.time.LocalDate;
import java.util.function.Supplier;
public class SupplierExample {
public static void main(String[] args) {
Supplier<LocalDate> currentDateSupplier = () -> LocalDate.now();
System.out.println(currentDateSupplier.get());
// 出力: 現在の日付(例: 2025-01-18)
}
}
※LocalDate型でなくSupplier<LocalDate>型として、他のメソッドに渡すことにより、Supplier.get()を実行した時点での現在日付が取得できるため、より正確な日付をメソッド内で扱うことができます。このような手法を遅延生成と呼び、値が実際に必要になるまで生成しないことを意味します。。

この関数型インターフェースを表現する方法として、「ラムダ式」と「メソッド参照」があります。上のサンプルコードでも扱ってきたラムダ式から解説します。
ラムダ式での表現方法
ラムダ式とは?
ラムダ式は、匿名クラスを簡潔に記述する方法です。特に関数型インターフェイスを簡単に実装するために使用されます。
サンプルコード
以下は、Consumerを使ったラムダ式の例です:
Consumer<String> printer = message -> System.out.println(message);
printer.accept("Hello, Java!");
メソッド参照の使い方
メソッド参照の基本
メソッド参照は、既存メソッドを利用して、ラムダ式をさらに簡潔に記述できる構文です。以下の3種類があります。
サンプルコード
次のシナリオを考えます。
- 商品名(name)を元に、複数の
Productオブジェクトをリストに変換したいとします。
- 商品情報(Product)を保持するクラスがあります。
この場合、コンストラクタ参照を使うことで簡潔に実装できます。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
class Product {
private String name;
// コンストラクタ
public Product(String name) {
this.name = name;
}
@Override
public String toString() {
return "Product{name='" + name + "'}";
}
}
public class ConstructorReferenceExample {
public static void main(String[] args) {
// 商品名のリスト
List<String> productNames = Arrays.asList("Laptop", "Smartphone", "Tablet");
// コンストラクタ参照を使ってProductオブジェクトを生成
List<Product> products = productNames.stream()
.map(Product::new) // コンストラクタ参照
.collect(Collectors.toList());
// 結果を出力
products.forEach(System.out::println);
// 出力結果
// Product{name='Laptop'}
// Product{name='Smartphone'}
// Product{name='Tablet'}
}
}
Stream APIでの活用
Stream APIとは?
Stream APIはJava 8で導入されたコレクション処理のための新しい方法です。近年のJava開発においては、必ずと言っていいほど利用されるもので、関数型インターフェイスが頻繁に使用されます。
実践的な例
以下は、Stream APIで関数型インターフェイスを活用したコード例です。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream()
.filter(n -> n % 2 == 0) // Predicate
.map(n -> n * n) // Function
.forEach(System.out::println); // Consumer
ポイント
Stream APIに関しては、filterに関しての記事やmapに関しての記事を参考にしてみてください。
まとめ
本記事では、Java 8で導入された関数型インターフェイスについて、基礎から応用までを詳しく解説しました。Consumer<T>やPredicate<T>といった基本的なインターフェイスの使い方から、ラムダ式やメソッド参照を用いた簡潔な記述方法、さらにはStream APIでの活用例までを紹介しました。
| インターフェイス | 役割 | 主要メソッド | 例 |
|---|---|---|---|
Consumer<T> | 処理を実行 | accept(T t) | UIの更新やログ出力 |
Predicate<T> | 条件を評価 | test(T t) | フィルタ処理 |
Function<T, R> | データ変換 | apply(T t) | 文字列を数値に変換する |
Supplier<T> | 値を生成 | get() | 現在の日付やランダム値を生成する |
関数型インターフェイスは、Javaコードを簡潔で効率的に記述するための強力なツールです。本記事で紹介した内容を活用し、より洗練されたJavaプログラミングに挑戦してみてください!
