この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:3
ページ更新者:guest
更新日時:2026-06-11 07:07:02

タイトル: ラムダ式
SEOタイトル: Java ラムダ式完全ガイド(Functional Interface / Method Reference / Stream)

この記事の要点
  • Java ラムダ式: (x, y) -> x + y。Java 8+ で導入
  • ラムダの型は関数型インタフェース(Functional Interface = SAM, 抽象メソッド 1 個)
  • 標準提供: Function / Predicate / Consumer / Supplier / BiFunction
  • Method Reference: String::length / System.out::println でラムダを簡潔に
  • ラムダから参照する外部変数はeffectively final である必要(事実上 final)

ラムダ式の基本

// 引数なし
Runnable r = () -> System.out.println("Hello");

// 引数 1 個(括弧省略可)
Function square = x -> x * x;

// 引数 2 個
BinaryOperator add = (x, y) -> x + y;

// 複数行はブロックと return
Function classify = n -> {
    if (n > 0) return "positive";
    if (n < 0) return "negative";
    return "zero";
};

// 型を明示することも可能
Function f = (Integer x) -> x * 2;

Functional Interface(関数型インタフェース)

ラムダの型は抽象メソッドを 1 つだけ持つインタフェース(SAM: Single Abstract Method)。コンパイラがそのメソッドにマッピングします。

@FunctionalInterface
interface StringTransformer {
    String transform(String input);
    // default / static メソッドは持ってよい
    default String transformUpper(String s) {
        return transform(s).toUpperCase();
    }
}

StringTransformer reverse = s -> new StringBuilder(s).reverse().toString();
System.out.println(reverse.transform("hello"));  // olleh

// @FunctionalInterface はチェック用アノテーション
// 抽象メソッドが 2 個以上だとコンパイルエラー

標準の関数型インタフェース(java.util.function)

インタフェースシグネチャ用途
FunctionR apply(T t)T → R の変換
BiFunctionR apply(T t, U u)2 引数
Predicateboolean test(T t)真偽判定(フィルタ)
Consumervoid accept(T t)副作用のみ(forEach 等)
SupplierT get()遅延生成 / ファクトリ
UnaryOperatorT apply(T t)T → T (Function 特殊版)
BinaryOperatorT apply(T t1, T t2)(T, T) → T (sum 等)
import java.util.function.*;

Function length = String::length;
Predicate isEmpty = String::isEmpty;
Consumer printer = System.out::println;
Supplier> newList = ArrayList::new;
BiFunction max = Math::max;

System.out.println(length.apply("hello"));   // 5
System.out.println(isEmpty.test(""));        // true
printer.accept("hello");                     // hello
System.out.println(max.apply(3, 7));         // 7

Method Reference(メソッド参照)

種類ラムダ等価
static メソッドInteger::parseInts -> Integer.parseInt(s)
インスタンスメソッド(特定)System.out::printlnx -> System.out.println(x)
インスタンスメソッド(任意)String::lengths -> s.length()
コンストラクタArrayList::new() -> new ArrayList()

effectively final(事実上 final)

ラムダから外側のローカル変数を参照する場合、その変数はfinal または事実上 final(一度しか代入されない)でなければなりません。

public void demo() {
    int base = 10;                          // effectively final(再代入なし)
    Function f = x -> x + base;  // OK

    int counter = 0;
    // counter++;  // ❌ これを書くと f がコンパイルエラー
    Runnable r = () -> System.out.println(counter);

    // インスタンス変数 (this.field) は制限なし
    this.value = 100;
}

// 回避策: 配列 or AtomicInteger
int[] counter = {0};
Runnable r = () -> counter[0]++;

Stream API での活用

List names = List.of("taro", "jiro", "saburo", "shiro");

String result = names.stream()
    .filter(s -> s.length() > 4)                  // Predicate
    .map(String::toUpperCase)                     // Function (Method Reference)
    .sorted(Comparator.comparingInt(String::length))
    .collect(Collectors.joining(", "));

System.out.println(result);  // SABURO, SHIRO

無名内部クラスとの違い

// Java 7 以前: 無名内部クラス
Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello");
    }
};

// Java 8+: ラムダ
Runnable r2 = () -> System.out.println("Hello");
項目無名内部クラスラムダ
this の意味無名クラスのインスタンス★ 囲み側の this
クラスファイル追加で生成されるinvokedynamic で軽量
抽象メソッド数制限なし1 つだけ(SAM)
外側変数アクセスfinal 必須(Java 7 まで)effectively final

他言語との比較

Java:    (x, y) -> x + y
Python:  lambda x, y: x + y
PHP 7+:  fn ($x, $y) => $x + $y          // arrow function
PHP:     function ($x, $y) { return $x + $y; }
JS:      (x, y) => x + y                  // ES6 arrow
Kotlin:  { x, y -> x + y }
C#:      (x, y) => x + y
Scala:   (x: Int, y: Int) => x + y

PHP の arrow functions との対応

// PHP 7.4+ arrow function
$add = fn ($x, $y) => $x + $y;
echo $add(1, 2);  // 3

// 外側変数を自動キャプチャ(use は不要)
$base = 10;
$addBase = fn ($x) => $x + $base;

// 通常の無名関数では use が必要
$addBase2 = function ($x) use ($base) {
    return $x + $base;
};

FAQ

Q: ラムダ式に clone はある?
A: ラムダは Cloneable ではなく clone() 呼び出しは未定義動作です。状態を持たない関数として扱ってください。

Q: ラムダで例外を投げたい(チェック例外)
A: 標準 Functional Interface はチェック例外を投げられません。独自の @FunctionalInterface を定義するか、ラムダ内で try/catch して RuntimeException でラップ。

Q: ラムダのデバッグがしづらい
A: スタックトレースに lambda$method$0 のような自動生成名が出ます。複雑なロジックは private メソッドに切り出して Method Reference 化を推奨。