🐨

ラムダ式の話

に公開

この記事は、以下のYouTubeプレイリストを私なりに要約したものです。
引用元の動画の方がラムダ式について非常に分かりやすく解説されているので、まずはそちらをご覧いただくのが一番理解しやすいと思います。

https://youtube.com/playlist?list=PL0BiAlg8j4Zu_XVmBIq2DTSP8LdRWSjwV&si=3tR1GWIr73J-vh33

はじめに

Javaにおいて、ラムダ式はコードを短く、読みやすくするだけでなく、設計の柔軟性や変更への強さを高める重要な仕組みです。しかし、いきなりラムダ式に飛びつくと理解が難しく感じます。(私の体験談)
実際、ラムダ式はJavaの進化の中で登場したもので、その背景には情報隠蔽、インターフェース、内部クラス、匿名クラスといった仕組みがあります。本記事では、その進化の流れを追いながら、最終的にラムダ式までをまとめます。

情報隠蔽から始める

まず理解しておきたいのが情報隠蔽という原則です。
情報隠蔽とは、クラスの利用者に実装の詳細を見せない設計手法です。

  • インターフェースで利用側と実装を分離する
  • ファクトリークラスを使って生成処理を隠す
  • パッケージやアクセス修飾子で外部からの不要な参照を制御する

こうした仕組みにより、実装を差し替えても利用コードはそのまま動かすことができます。これがポリモーフィズムの基本的な活用です。

インナークラスと匿名クラス

次の段階で登場するのがインナークラスです。
特にプライベートインナークラスを使えば、同じパッケージからすらアクセスできない完全な情報隠蔽が可能です。

さらにJavaはこれを発展させ、匿名クラスという書き方ができるようになりました。匿名クラスでは、

  • クラスの名前すら定義せず
  • その場でインターフェースを実装したインスタンスを生成できる

ようになります。
匿名クラスを使うことで、ちょっとした一時的な実装をその場で定義できるようになり、コードを整理しやすくなりました。
ただし、入れ子が深くなると可読性が下がるため、使いすぎには注意が必要です。

匿名クラスからラムダ式へ

しかし、匿名クラスにも弱点があります。
インターフェースを1つ実装するだけなのに、わざわざnewを書き、メソッドをオーバーライドしなければなりません。

この冗長さを解消するために登場したのがラムダ式です。
ラムダ式では、クラス定義もnewも不要で、関数の中身だけをシンプルに書けます。

例:

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

// ラムダ式
Runnable task = () -> System.out.println("Hello World!");

たったこれだけの違いで、コードの可読性が大きく向上します。

関数型インターフェース

ラムダ式が使えるのは、抽象メソッドが1つだけのインターフェース、すなわち関数型インターフェースです。

代表例はjava.util.functionパッケージに揃っています。

  • Function<T, R>:入力を加工して結果を返す
  • Supplier<T>:値を供給する
  • Consumer<T>:値を消費する(処理する)
  • Predicate<T>:真偽値を判定する

さらに、安全のために@FunctionalInterfaceアノテーションを付けておくと、間違えて抽象メソッドを増やすことを防げます。

設計原則とラムダ式

ラムダ式は単なる省略記法ではなく、設計原則とも深く関係します。

  • 単一責任の原則:メイン処理と可変部分を明確に分離し、変更が必要な部分だけをラムダとして差し替えられる
  • ストラテジーパターン:処理ロジックをラムダで差し替え可能
  • 関心事の分離:定型的な処理と固有の処理を分けられる

これにより、変更に強いプログラムが書け、保守性が大幅に向上します。

メソッドチェーンとの併用

実践的な使い方として、メソッドチェーンとの組み合わせがあります。

セッターメソッドの戻り値をthisにすれば、次のように書けます。↓

Product p = new Product()
    .setName("Apple")
    .setPrice(100)
    .setStock(50);

さらに、Consumer<Product>を利用すれば、ラムダ式内でまとめて初期化処理を書くことも可能です。↓

public static Product create(Consumer<Product> initializer) {
    Product p = new Product();
    initializer.accept(p);
    return p;
}

// 呼び出し側
Product apple = Product.create(p -> {
    p.setName("Apple");
    p.setPrice(100);
    p.setStock(50);
});

これにより、オブジェクト生成から初期化までを直感的に表現できます。

まとめ

Javaのラムダ式は、単なるコードを書くことを省略するための記法ではなく、情報隠蔽 → インナークラス → 匿名クラス → ラムダ式という歴史的な進化の先にある仕組みです。

  • コードを短く、読みやすくする
  • 設計原則を実現し、変更に強いプログラムを作る
  • java.util.functionを活用して実践的に使える

参考記事

https://qiita.com/kenRp01/items/4045a7925340088bd7e3

Discussion