本ページはプロモーションが含まれています

Java入門

Javaラムダ式を理解!基本から具体例までわかりやすく解説

トム

・SaaS勤務/javaのバックエンドエンジニア
/java歴10年以上 ・首都圏在住30代
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎

「Javaのコードをもっとすっきりさせたい」

「ラムダ式ってよく聞くけど、なんだか難しそう…」

Java開発に携わっていると、一度はラムダ式について耳にしたことがあるのではないでしょうか。私自身、Javaでの開発経験が10年以上ありますが、Java 8でラムダ式が導入された当初は、その独特な書き方に少し戸惑った経験があります。しかし、一度その便利さを知ってからは、もはやラムダ式なしのJavaコーディングは考えられないほどになりました。

この記事は、過去の私と同じように、Javaのラムダ式に苦手意識を持っている初心者の方や、これからラムダ式を学ぼうとしている方に向けて書いています。

この記事を読み終える頃には、あなたは以下の状態になっています。

  • Javaラムダ式の基本的な書き方がわかる
  • ラムダ式をどのような場面で使えるか理解できる
  • 実際のコードにラムダ式を取り入れて、簡潔なコードを書けるようになる

専門用語をできるだけ避け、豊富なコード例とともにJavaのラムダ式をテーマに解説していくので、ぜひ最後までお付き合いください。

Javaのラムダ式とは?

Javaのラムダ式は、一言でいうと「名前のない関数」です。メソッドを定義するとき、通常はアクセス修飾子や戻り値の型、メソッド名を指定します。しかしラムダ式を使えば、それらを省略して処理内容だけを簡潔に記述できるのです。

この仕組みは、Java 8で導入されたもので、コードをよりシンプルにし、とくに関数型プログラミングのスタイルをJavaで実現しやすくするために追加されました。

ラムダ式の基本的な書き方

ラムダ式の基本形は非常にシンプルです。

(引数) -> {処理内容}

このように、丸かっこ () で引数を定義し、矢印 -> を挟んで、波かっこ {} の中に具体的な処理を記述します。まるで矢印が引数を処理に渡しているようなイメージで覚えると分かりやすいかもしれません。

無名クラスとの違い

ラムダ式が登場する前は、「無名クラス(匿名クラス)」という仕組みを使って似たような処理を実現していました。しかし、無名クラスは記述が冗長になりがちです。

例えば、ボタンがクリックされたときの処理を考えてみましょう。

【無名クラスを使った従来の書き方】

button.setOnAction(new EventHandler<ActionEvent>() {
    @Override
    public void handle(ActionEvent event) {
        System.out.println("ボタンがクリックされました");
    }
});

やりたいことは「ボタンがクリックされたらメッセージを表示する」だけなのに、その周りの記述が多くて少しわかりにくいですね。

これをラムダ式で書くと、驚くほど簡潔になります。

【ラムダ式を使った書き方】

button.setOnAction(event -> System.out.println("ボタンがクリックされました"));

いかがでしょうか。やりたい処理の本質だけが残り、コードが非常にすっきりとしました。これがラムダ式の大きな力です。

ラムダ式が導入された背景(Java 8)

ラムダ式がJava8で導入されたのには、2つの大きな理由があります。

理由

  1. コードの簡潔化: 先ほどの例で見たように、無名クラスなどの冗長な記述を減らし、よりシンプルで読みやすいコードを書けるようにするためです。
  2. 並列処理の効率化: 近年のCPUは複数のコアを持つのが当たり前になりました。ラムダ式と、同時に導入された「Stream API」を組み合わせると、コレクション(リストなど)の要素に対する処理を複数のコアで並列実行しやすくなります。これにより、大量のデータを扱う処理のパフォーマンスを向上させられるのです。

Javaのラムダ式の使い方

ラムダ式の便利さがわかったところで、次は具体的な書き方のルールを見ていきましょう。いくつかの省略ルールを覚えると、さらにコードを短くできます。

基本構文と記述ルール

ラムダ式には、状況に応じて記述をさらに簡潔にするためのルールが存在します。

  • 引数の型は省略可能: コンパイラが文脈から型を推論できる場合、引数の型を省略できます。(String s) -> s.length() は (s) -> s.length() と書けます。
  • 引数が1つの場合、丸かっこ () を省略可能:(s) -> s.length() は s -> s.length() と書けます。
  • 処理が1行の場合、波かっこ {} と return を省略可能:s -> { return s.length(); } は s -> s.length() と書けます。

これらのルールを組み合わせることで、非常に短いコードで処理を表現できるようになるのです。

引数あり・なしの例

ラムダ式は、引数の数に応じて柔軟に記述できます。

【引数がない場合】

引数がない場合は、丸かっこ () を省略できません。必ず記述する必要があります。

// Runnableインターフェースの実装
Runnable runner = () -> System.out.println("タスクを実行します");
runner.run(); // "タスクを実行します" と表示される

【引数が1つの場合】

先ほどのルール通り、丸かっこ () を省略できます。

// 文字列を受け取り、その長さを表示する処理
Consumer<String> consumer = s -> System.out.println(s.length());
consumer.accept("Java"); // 4 と表示される

【引数が2つの場合】

引数が2つ以上ある場合は、丸かっこ () で囲む必要があります。

// 2つの整数を受け取って足し算する処理
BinaryOperator<Integer> operator = (a, b) -> a + b;
int result = operator.apply(10, 20);
System.out.println(result); // 30 と表示される

戻り値を持つ場合の例

処理の結果として値を返す場合は、return キーワードを使います。

// 2つの数値を比較して大きい方を返す
BinaryOperator<Integer> max = (a, b) -> {
    if (a > b) {
        return a;
    } else {
        return b;
    }
};

そして、ここでも先ほどの省略ルールが役立ちます。処理が1行で完結する場合、波かっこ {}return を省略可能です。

// 2つの数値を足し算した結果を返す (returnが省略されている)
BinaryOperator<Integer> sum = (a, b) -> a + b;
int result = sum.apply(5, 3); // resultには 8 が入る

この省略形は非常によく使われるので、ぜひ覚えておきましょう。

Javaのラムダ式の具体例

それでは、実際の開発現場でラムダ式がどのように使われるのか、具体的な例を3つ見ていきましょう。これらの例を見ることで、ラムダ式の活用イメージがより具体的になるはずです。

リスト処理(forEach)で使う

リスト(List)の全ての要素に対して同じ処理を行いたい場合、これまでは拡張for文がよく使われてきました。

【拡張for文を使った従来の書き方】

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
for (String name : names) {
    System.out.println(name);
}

この処理は、forEach メソッドとラムダ式を使うことで、より直感的に記述できます。

【forEachとラムダ式を使った書き方】

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println(name));

「namesリストの各要素(name)に対して、それを表示する」という処理内容が、コードから直接的に読み取れます。

Stream APIと組み合わせる

ラムダ式が最もその真価を発揮するのが、Java 8で導入された Stream API との組み合わせです。Stream APIを使うと、水の流れのようにコレクションの要素を次々と処理できます。

例えば、「フルーツのリストから、文字数が5文字以上のものだけを抜き出し、大文字に変換して、新しいリストを作る」という処理を考えてみましょう。

【従来の書き方】

List<String> fruits = Arrays.asList("apple", "banana", "orange", "grape", "kiwi");
List<String> result = new ArrayList<>();
for (String fruit : fruits) {
    if (fruit.length() >= 5) {
        result.add(fruit.toUpperCase());
    }
}
// result: ["APPLE", "BANANA", "ORANGE", "GRAPE"]

このコードも間違いではありませんが、処理の途中で結果を格納するための新しいリストを用意する必要があり、少し手間がかかります。

これをStream APIとラムダ式で書き換えてみましょう。

【Stream APIとラムダ式を使った書き方】

List<String> fruits = Arrays.asList("apple", "banana", "orange", "grape", "kiwi");
List<String> result = fruits.stream()
                            .filter(f -> f.length() >= 5)    // 5文字以上を絞り込む
                            .map(f -> f.toUpperCase())      // 大文字に変換する
                            .collect(Collectors.toList());  // 結果をリストにする
// result: ["APPLE", "BANANA", "ORANGE", "GRAPE"]

filter(絞り込み)や map(変換)といった操作がメソッドチェーンで繋がっており、データがどのように処理されていくのかが一目瞭然です。このように、複雑なデータ処理もラムダ式を使えば宣言的に、そしてわかりやすく記述できます。

Comparatorでのソート処理

オブジェクトのリストを特定のルールで並び替えたい場合、Comparator インターフェースを使います。これも以前は無名クラスで記述するのが一般的でした。

【無名クラスを使ったソート処理】

List<String> names = Arrays.asList("Charlie", "Alice", "Bob");
Collections.sort(names, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.compareTo(b); // 文字列の昇順でソート
    }
});

このソート処理も、ラムダ式を使えばたった1行で書けてしまいます。

【ラムダ式を使ったソート処理】

List<String> names = Arrays.asList("Charlie", "Alice", "Bob");
Collections.sort(names, (a, b) -> a.compareTo(b));
// さらにメソッド参照を使えばもっと短く書けます
// Collections.sort(names, String::compareTo);

比較のロジック (a, b) -> a.compareTo(b) だけを記述すればよく、非常に簡潔です。

ラムダ式を使うメリットと注意点

ラムダ式は非常に強力な機能ですが、良い面だけでなく、いくつか注意すべき点もあります。両方を理解して、適切に使いこなしましょう。

メリット1:コードが簡潔になる

これはこれまで見てきた通り、ラムダ式の最大のメリットです。無名クラスなどの定型的なコードを大幅に削減し、処理の本質に集中したコーディングを可能にします。コード量が減ることで、ファイル全体の見通しも良くなるでしょう。

メリット2:可読性・保守性の向上

コードが簡潔になることは、可読性の向上に直結します。「何をしているのか」がすぐに理解できるコードは、将来の自分や他の開発者にとっても優しく、結果的に保守性の高いコードに繋がります。とくにStream APIと組み合わせた際の処理の流れのわかりやすさは、特筆すべき点です。

注意点:デバッグや例外処理の難しさ

一方で、ラムダ式には注意点もあります。その一つがデバッグの難しさです。ラムダ式は「名前のない関数」なので、例外が発生した際のスタックトレース(エラーの発生箇所を示すログ)に、私たちがつけたメソッド名が表示されません。そのため、エラーの原因特定に少し時間がかかる場合があります。

また、ラムダ式の中からチェック例外(IOExceptionなど、処理が必須の例外)をスローする場合、少し工夫が必要になる点も覚えておきましょう。

まとめ:Javaのラムダ式を使いこなそう

今回は、Javaのラムダ式について、基本的な書き方から具体的な活用例、メリット・注意点までをわかりやすく解説しました。

初心者がまず練習すべきポイント

もしあなたがラムダ式初心者なら、まずはリストの forEach メソッドで使ってみることをお勧めします。普段 for ループで書いている処理をラムダ式で書き換える練習を繰り返すことで、その記法に自然と慣れていくでしょう。

慣れてきたら、次は Stream API の filtermap に挑戦してみてください。コレクションの操作が驚くほど簡単で楽しくなるはずです。

ラムダ式と今後のJava開発

Java 8で導入されて以来、ラムダ式はJavaプログラミングの標準的な機能となりました。現代のJava開発において、ラムダ式やStream APIの知識はもはや必須といっても過言ではありません。

最初は少し難しく感じるかもしれませんが、使いこなせればあなたのコードはより洗練され、開発効率も格段に向上するはずです。

  • この記事を書いた人
  • 最新記事

トム

・SaaS勤務/javaのバックエンドエンジニア
/java歴10年以上 ・首都圏在住30代
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎

-Java入門