モダンなJavaの書き方①:ラムダ式

Java 8 で導入されたラムダ式の書き方と使い方を学びます。

モダン 1 2 3 4

モダンなJavaの書き方①:ラムダ式

ラムダ式とは

ラムダ式(Lambda Expression)は Java 8 で導入された機能で、メソッドを短く簡潔に表現できる書き方です。処理を「値として渡す」ことができ、無名クラス(匿名クラス)を使っていた冗長なコードを大幅に短縮できます。

ラムダ式は 関数型インターフェース(抽象メソッドが1つだけのインターフェース)に対してのみ使用できます。

基本構文

ラムダ式の基本的な書き方は以下の通りです。

書式
(引数) -> { 処理; } // 引数が1つの場合は括弧を省略できる 引数 -> { 処理; } // 処理が1行の場合は {} と return を省略できる (引数) -> 式

無名クラスとの比較

ラムダ式導入以前は、インターフェースを実装するために無名クラスを使っていました。

Java(無名クラスを使った書き方)
import java.util.ArrayList;
import java.util.Collections;

ArrayList<String> list = new ArrayList<>();
list.add("banana");
list.add("apple");
list.add("cherry");

// 無名クラスで Comparator を実装
Collections.sort(list, new java.util.Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.compareTo(b);
    }
});
System.out.println(list);
Java(ラムダ式を使った書き方)
import java.util.ArrayList;
import java.util.Collections;

ArrayList<String> list = new ArrayList<>();
list.add("banana");
list.add("apple");
list.add("cherry");

// ラムダ式で同じ処理を1行に
Collections.sort(list, (a, b) -> a.compareTo(b));
System.out.println(list);
実行結果
[apple, banana, cherry]

関数型インターフェース

Java 8 では java.util.function パッケージに代表的な関数型インターフェースが用意されています。

インターフェース抽象メソッド用途
Runnablevoid run()引数なし・戻り値なしの処理
Consumer<T>void accept(T t)引数あり・戻り値なしの処理
Supplier<T>T get()引数なし・値を返す処理
Function<T, R>R apply(T t)引数を変換して返す処理
Predicate<T>boolean test(T t)条件を判定する処理

Consumer を使ったサンプル

Consumer は「受け取って処理するだけ、値は返さない」インターフェースです。

Java
import java.util.function.Consumer;

// 文字列を受け取って表示する Consumer
Consumer<String> printer = s -> System.out.println("入力値: " + s);
printer.accept("Hello");
printer.accept("Java");
実行結果
入力値: Hello
入力値: Java

Predicate を使ったサンプル

Predicate は「条件を判定して true/false を返す」インターフェースです。

Java
import java.util.function.Predicate;

// 偶数かどうかを判定する Predicate
Predicate<Integer> isEven = n -> n % 2 == 0;

System.out.println(isEven.test(4));   // true
System.out.println(isEven.test(7));   // false

// Predicate の合成(and / or / negate)
Predicate<Integer> isPositive = n -> n > 0;
Predicate<Integer> isPositiveEven = isEven.and(isPositive);

System.out.println(isPositiveEven.test(6));   // true
System.out.println(isPositiveEven.test(-4));  // false(負の数)
実行結果
true
false
true
false

Function を使ったサンプル

Function は「値を受け取って変換した値を返す」インターフェースです。

Java
import java.util.function.Function;

// 整数を受け取り、その2乗を返す Function
Function<Integer, Integer> square = n -> n * n;
System.out.println(square.apply(5));   // 25

// Function の合成(andThen)
Function<String, String> trim   = s -> s.trim();
Function<String, String> upper  = s -> s.toUpperCase();
Function<String, String> clean  = trim.andThen(upper);

System.out.println(clean.apply("  hello  "));  // HELLO
実行結果
25
HELLO

独自の関数型インターフェース

@FunctionalInterface アノテーションを付けると、独自の関数型インターフェースを定義できます。

Java
@FunctionalInterface
interface Calculator {
    int calc(int a, int b);
}

// ラムダ式で実装
Calculator add = (a, b) -> a + b;
Calculator mul = (a, b) -> a * b;

System.out.println("足し算: " + add.calc(3, 4));  // 7
System.out.println("掛け算: " + mul.calc(3, 4));  // 12
実行結果
足し算: 7
掛け算: 12

メソッド参照

既存のメソッドをラムダ式の代わりに直接参照する メソッド参照 という書き方もあります。

書式
// 静的メソッド参照 クラス名::メソッド名 // インスタンスメソッド参照 インスタンス::メソッド名 // コンストラクタ参照 クラス名::new
Java
import java.util.Arrays;
import java.util.List;

List<String> list = Arrays.asList("banana", "apple", "cherry");

// ラムダ式による表示
list.forEach(s -> System.out.println(s));

// メソッド参照による表示(上と同じ動作)
list.forEach(System.out::println);
実行結果
banana
apple
cherry
まとめ
・ラムダ式は (引数) -> 処理 の形で書く
・関数型インターフェース(抽象メソッドが1つ)に対してのみ使える
ConsumerPredicateFunction など標準の関数型インターフェースを活用する
・メソッド参照 クラス::メソッド でさらに簡潔に書ける
・次の Stream API と組み合わせると真価を発揮する