👌

Java Gold ラムダ式(関数)について

2024/11/24に公開

ラムダ式とは何か

ラムダ式は、匿名関数(名前を持たない関数)を簡潔に表現するための構文です。従来、匿名クラスを使用して記述していたコードを、よりシンプルに書くことができます。これにより、コードの可読性が向上し、開発効率もアップします。

例:匿名クラスとラムダ式の比較

// 匿名クラスを使用
Runnable runnable = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello, World!");
    }
};

// ラムダ式を使用
Runnable runnable = () -> System.out.println("Hello, World!");

ラムダ式の基本構文

ラムダ式の基本的な構文は以下のとおりです。

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

引数リスト:関数が受け取る引数をカンマで区切って列挙します。
矢印演算子(->):ラムダ式であることを示します。
処理内容:メソッドの本体を中括弧 {} 内に記述します。

シンプルな例

// 引数なし、戻り値なし
() -> System.out.println("Hello, Lambda!");

// 引数あり、戻り値なし
(String message) -> System.out.println(message);

// 引数あり、戻り値あり
(int x, int y) -> x + y;
省略可能な部分

引数の型はコンパイラが推論できる場合、省略可能。
引数が1つの場合、括弧 () を省略可能。
処理内容が単一の式の場合、中括弧 {} と return キーワードを省略可能。

省略例

// 型と括弧を省略
message -> System.out.println(message);
// 中括弧とreturnを省略
(x, y) -> x + y;

関数型インターフェース

ラムダ式は関数型インターフェースを実装するために使用されます。関数型インターフェースとは、抽象メソッドを1つだけ持つインターフェースのことです。

代表的な関数型インターフェース

Consumer<T>:引数を1つ取り、戻り値なし。(コンシューマー)
Function<T, R>:引数を1つ取り、戻り値あり。(ファンクション)
Predicate<T>:引数を1つ取り、boolean を返す。(プレディケート)
Supplier<T>:引数なし、戻り値あり。 (サプライヤー)
Comparator<T>:2つの引数を取り、整数を返す。(コンパレーター)

早見表

メソッド 役割 使用する関数型インターフェース
forEach 各要素に対する処理 Consumer< T >
map 各要素の変換 Function< T , R >
filter 条件に合う要素を抽出 Predicate< T >
sort 要素の並べ替え Comparator< T >
reduce 要素の集約 BinaryOperator< T >
ifPresent 値が存在する場合の処理 Consumer< T >
submit (スレッド) タスクの非同期実行 Runnable or Callable< V >
collect(toMap) リストやセットをマップに変換 Function< T , K >, Function< T, V >
peek 各要素に処理を行い、ストリームを返す Consumer< T >
allMatch すべての要素が条件を満たすか判定 Predicate< T >
anyMatch いずれかの要素が条件を満たすか判定 Predicate< T >
noneMatch すべての要素が条件を満たさないか判定 Predicate< T >
findFirst 最初の要素を取得 Supplier<Optional< T >>
findAny 任意の要素を取得(並列処理に適用) Supplier< Optional < T > >
max 最大値を取得 Comparator< T >
min 最小値を取得 Comparator< T >
groupingBy(Collectors) グループ化しマップに変換 Function< T , K >
partitioningBy(Collectors) 条件で2つのグループに分ける Predicate< T >
flatMap 要素を展開して一つのストリームにする Function< T, Stream< R>>

ラムダ式の活用例

コレクションの操作

forEachメソッド
コレクションの各要素に対して処理を行います。

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

sortメソッド
リストの要素を特定の基準で並べ替えます。

names.sort((a, b) -> a.length() - b.length());

ストリームAPIとの連携

mapメソッド
各要素に関数を適用して、新しいストリームを生成します。

List<Integer> nameLengths = names.stream()
    .map(name -> name.length())
    .collect(Collectors.toList());

filterメソッド
条件に合致する要素だけを抽出します。

List<String> shortNames = names.stream()
    .filter(name -> name.length() <= 3)
    .collect(Collectors.toList());

reduceメソッド
ストリームの要素を集約して1つの結果にまとめます。

int totalLength = names.stream()
    .map(name -> name.length())
    .reduce(0, (a, b) -> a + b);

並行処理での使用

Runnable(ラナブル)とCallable(カラブル)
スレッドやタスクの実行で使用されます。

ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> System.out.println("Task is running"));
executor.shutdown();

Optionalクラスとの組み合わせ

ifPresentメソッド(イフプレゼント)
値が存在する場合に処理を実行します。

Optional<String> optionalName = Optional.of("Alice");
optionalName.ifPresent(name -> System.out.println("Name: " + name));

mapメソッド
Optionalの値を変換します。

Optional<Integer> nameLength = optionalName.map(name -> name.length());

メソッド参照とラムダ式

ラムダ式が既存のメソッドをそのまま呼び出すだけの場合、メソッド参照を使用してより簡潔に記述できます。

シンタックス
ClassName::methodName
object::methodName

// ラムダ式を使用
names.forEach(name -> System.out.println(name));

// メソッド参照を使用
names.forEach(System.out::println);

Discussion