Java アドバンス

Java ラムダ式 (Lambda Expressions)

1. Java ラムダ式とは

ラムダ式(Lambda Expressions)は Java 8 で導入された機能です。

ラムダ式は、パラメータを受け取り値を返す短いコードブロックです。メソッドに似ていますが、名前を必要とせず、メソッドの本体の中に直接記述できるという特徴があります。

2. 構文(Syntax)

最もシンプルなラムダ式は、単一のパラメータと一つの式で構成されます。

parameter -> expression

複数のパラメータを使用する場合は、括弧で囲みます。

(parameter1, parameter2) -> expression

シンプルな式は即座に値を返す必要があります。ループや if 条件などの複数のステートメントを含めることはできません。より複雑な処理を行うには、波括弧 {} を使用してコードブロックを作成します。値を返す必要がある場合は、return キーワードを使用します。

(parameter1, parameter2) -> {
  // コードブロック
  return result;
}

3. ラムダ式の使用方法

ラムダ式は、メソッドの引数として渡されることがよくあります。例えば、ArrayListforEach() メソッド内でラムダ式を使用できます。

3.1 コード例:forEach() での利用

import java.util.ArrayList;

public class Main {
  public static void main(String[] args) {
    ArrayList<Integer> numbers = new ArrayList<Integer>();
    numbers.add(5);
    numbers.add(9);
    numbers.add(8);
    numbers.add(1);
    // 各要素 n を出力するラムダ式
    numbers.forEach((n) -> { System.out.println(n); });
  }
}

4. 変数への格納

ラムダ式は変数に格納することができます。ただし、その変数の型は、抽象メソッドを一つだけ持つインターフェース(関数型インターフェース / Functional Interface)である必要があります。また、ラムダ式はそのメソッドのパラメータと戻り値の型に一致していなければなりません。

Java には、リストで使用される java.util.function パッケージの Consumer など、多くの組み込み関数型インターフェースが含まれています。

4.1 コード例:Consumer 変数への格納

import java.util.ArrayList;
import java.util.function.Consumer;

public class Main {
  public static void main(String[] args) {
    ArrayList<Integer> numbers = new ArrayList<Integer>();
    numbers.add(5);
    numbers.add(9);
    numbers.add(8);
    numbers.add(1);
    
    // ラムダ式を変数 method に格納
    Consumer<Integer> method = (n) -> { System.out.println(n); };
    numbers.forEach(method);
  }
}

5. メソッドパラメータとしてのラムダ式

ラムダ式をメソッドに渡すことも可能です。そのメソッドのパラメータは関数型インターフェースである必要があります。インターフェースのメソッドを呼び出すことで、渡されたラムダ式が実行されます。

5.1 コード例:自作インターフェースでの利用

// 関数型インターフェースの定義
interface StringFunction {
  String run(String str);
}

public class Main {
  public static void main(String[] args) {
    // 異なる処理を行うラムダ式を定義
    StringFunction exclaim = (s) -> s + "!";
    StringFunction ask = (s) -> s + "?";
    
    printFormatted("こんにちは", exclaim);
    printFormatted("こんにちは", ask);
  }

  // 関数型インターフェースを引数に取るメソッド
  public static void printFormatted(String str, StringFunction format) {
    String result = format.run(str);
    System.out.println(result);
  }
}

6. 匿名クラス vs ラムダ式

Java 8 以降では、多くの場合、匿名クラス(Anonymous Class)をラムダ式に置き換えることができます。ただし、これが可能なのはインターフェースが関数型インターフェース(抽象メソッドが一つだけ)である場合に限られます。

6.1 匿名クラスによる実装

// 関数型インターフェース(抽象メソッドが一つ)
interface Greeting {
  void sayHello();
}

public class Main {
  public static void main(String[] args) {
    Greeting g = new Greeting() {
      public void sayHello() {
        System.out.println("匿名クラスからの挨拶");
      }
    }; 
    g.sayHello();
  }
}

6.2 ラムダ式による実装

// 同じ関数型インターフェース
interface Greeting {
  void sayHello();
}

public class Main {
  public static void main(String[] args) {
    // ラムダ式で簡潔に記述
    Greeting g = () -> System.out.println("ラムダ式からの挨拶");
    g.sayHello();
  }
}

6.3 使い分けの指針

  • ラムダ式: 短く、単一のメソッドを持つインターフェースの実装に適しています。
  • 匿名クラス: 複数のメソッドをオーバーライドする必要がある場合、フィールドを追加する場合、またはクラスを継承する場合には匿名クラスを使用します。