Javaラムダ式完全ガイド:基礎からStream APIまで
Javaラムダ式をマスターする
Javaのラムダ式、関数型インターフェース、ジェネリクス、Stream APIを明確な例で探求します。
Java 8で導入されたラムダ式は言語の表現力を高め、開発者がより簡潔で読みやすいコードを書けるようにしました。
ラムダに入る前に、ラムダ実装の基盤となる関数型インターフェースを理解することが不可欠です。
関数型インターフェースとは?
関数型インターフェースは、ちょうど1つの抽象メソッドを含むJavaインターフェースです。この単一メソッドがインターフェースの意図する目的を定義します。
java.langのRunnableインターフェースは古典的な例で、run()メソッドのみを公開します。
例1:関数型インターフェースの定義
import java.lang.FunctionalInterface;
@FunctionalInterface
public interface MyInterface {
// 単一の抽象メソッド
double getValue();
}
ここではMyInterfaceに@FunctionalInterfaceアノテーションが付与され、コンパイラに関数型インターフェース契約を強制させ、複数の抽象メソッドの誤った追加を防ぎます。アノテーションは任意ですが、明確性と安全性のため強く推奨されます。
Java 7では関数型インターフェースはSingle Abstract Methods(SAM)と呼ばれ、通常は匿名クラスで実装されていました。
例2:匿名クラスによるSAM実装
public class FunctionInterfaceTest {
public static void main(String[] args) {
// Runnable関数型インターフェースを実装する匿名クラス
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Runnable関数型インターフェースを実装しました。");
}
}).start();
}
}
出力:
Runnable関数型インターフェースを実装しました。
匿名クラスは従来の実装よりボイラープレートを削減しますが、構文は依然として冗長です。ラムダ式は関数型インターフェースを引数として渡す際に明示的なメソッド名の必要性を排除します。
ラムダ式入門
ラムダ式は本質的に匿名メソッドで、関数型インターフェースの抽象メソッドの簡潔な実装を提供します。独立して実行されるのではなく、インターフェースのメソッドの振る舞いを供給します。
Javaでのラムダ式の定義方法
一般的な構文は:
(parameter list) -> lambda body
アロー演算子(->)がパラメータリストとボディを分離。具体例を以下に示します。
円周率を返すメソッドがあるとします:
double getPiValue() {
return 3.1415;
}
ラムダを使用:
() -> 3.1415
パラメータなしの場合、アローの左側は空、右側は値を返す式ボディになります。
ラムダボディの種類
Javaラムダボディには2つの形式があります:
1. 式ボディ – 単一の式を含む。
() -> System.out.println("Lambdas are great");
2. ブロックボディ – 波括弧でカプセル化され、複数ステートメントと任意のreturnを許可。
() -> { double pi = 3.1415; return pi; };
注意: 式ボディは明示的return不要、ブロックボディは値を返す場合return必須。
例3:ラムダ式の実践
ラムダ式を使用して円周率を返すJavaプログラムを作成:
import java.lang.FunctionalInterface;
@FunctionalInterface
interface MyInterface {
double getPiValue();
}
public class Main {
public static void main(String[] args) {
MyInterface ref = () -> 3.1415;
System.out.println("Piの値 = " + ref.getPiValue());
}
}
出力:
Piの値 = 3.1415
重要ポイント:
MyInterfaceは単一の抽象メソッドを定義MyInterfaceへの参照を宣言するが直接インスタンス化しない- ラムダ式が
getPiValue()の実装を提供 ref.getPiValue()呼び出しでラムダボディが実行
パラメータ付きラムダ式
メソッドと同様にラムダもパラメータを受け取れます。例:
(n) -> (n % 2) == 0
このラムダは入力整数が偶数かどうかをチェックします。
例4:パラメータ付きラムダで文字列反転
@FunctionalInterface
interface MyInterface {
String reverse(String s);
}
public class Main {
public static void main(String[] args) {
MyInterface ref = (str) -> {
String result = "";
for (int i = str.length() - 1; i >= 0; i--) {
result += str.charAt(i);
}
return result;
};
System.out.println("ラムダで反転 = " + ref.reverse("Lambda"));
}
}
出力:
ラムダで反転 = adbmaL
ジェネリック関数型インターフェース
関数型インターフェースはジェネリック化でき、任意のデータ型で動作可能。例:
@FunctionalInterface
interface MyInterface {
T func(T t);
}
例5:文字列と整数用のジェネリックラムダ
public class Main {
public static void main(String[] args) {
// 文字列反転
MyInterface reverse = (str) -> {
String result = "";
for (int i = str.length() - 1; i >= 0; i--) {
result += str.charAt(i);
}
return result;
};
System.out.println("ラムダで反転 = " + reverse.func("Lambda"));
// 整数階乗
MyInterface factorial = (n) -> {
int result = 1;
for (int i = 1; i <= n; i++) {
result *= i;
}
return result;
};
System.out.println("5の階乗 = " + factorial.func(5));
}
}
出力:
ラムダで反転 = adbmaL 5の階乗 = 120
ラムダ式とStream API
java.util.streamパッケージ(JDK 8導入)は、コレクションに対する関数型スタイル操作(フィルタリング、マッピング、リダクション、ソートなど)を可能にします。
各地名が国名でプレフィックスされたリストを考えます。ストリームとラムダを使用して効率的にデータを抽出・操作できます。
例6:ラムダを使用したストリーム処理
import java.util.ArrayList;
import java.util.List;
public class StreamMain {
static List places = new ArrayList<>();
public static List getPlaces() {
places.add("Nepa, Kathmandu");
places.add("Nepa, Pokhara");
places.add("India, Delhi");
places.add("USA, New York");
places.add("Africa, Nigeria");
return places;
}
public static void main(String[] args) {
List myPlaces = getPlaces();
System.out.println("ネパールの場所:");
myPlaces.stream()
.filter(p -> p.startsWith("Nepa"))
.map(p -> p.toUpperCase())
.sorted()
.forEach(p -> System.out.println(p));
}
}
出力:
ネパールの場所: NEPA, KATHMANDU NEPA, POKHARA
ここではストリーム操作の連鎖(filter、map、sorted、forEach)がそれぞれラムダを受け取り、Java 8以前のソリューションと比較してボイラープレートを大幅に削減しています。
Java
- C#インターフェイスをマスターする:定義、実装、実践的なユースケース
- Java演算子をマスター:種類、構文、実践例
- Javaの式、文、ブロックを理解する
- Javaインターフェース完全ガイド:概念、実装、ベストプラクティス
- Javaのtry-with-resourcesをマスターする:自動リソース管理の仕組みと活用法
- Javaアノテーション完全ガイド:種類、配置場所、実践例
- Java インターフェース vs 抽象クラス:適切な抽象化の選び方
- Java正規表現: java.util.regexでパターンマッチングをマスターする
- Javaインターフェース完全ガイド: 定義・実装・多重継承・タグインターフェース
- Java 9プライベートインターフェースメソッド:カプセル化とコード再利用を強化
- Java 8ラムダ式マスター:関数型プログラミング入門