本ページはプロモーションが含まれています

Java入門

【Java】匿名クラス(無名クラス)とは?使いどころを解説

トム

・都内自社開発企業勤務/Javaバックエンドエンジニア
/Java歴10年以上 ・首都圏在住30代
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎 詳細なプロフィール

Javaの匿名クラス(無名クラス)とは、クラス名を付けずにその場で定義・インスタンス化する使い捨てのクラスです。

new Runnable() { ... } のように、インターフェースや抽象クラスの実装をワンライナーで書けるため、イベント処理やコールバックなど一度きりの処理に重宝します。

私自身10年以上Javaで開発をしていますが、若手の頃は new の直後に { } が続く構文の意味がわからず戸惑いました。

この記事では、基本構文から3つの実践的な使用例、ラムダ式との使い分け基準まで、コード例付きで体系的に解説します。

この記事を読めば、以下の悩みを解決できます。

  • Javaの匿名クラス(無名クラス)が何なのか、基本から理解したい方
  • 匿名クラスがどのような場面で役立つのか、具体的な使い方を知りたい方
  • Java 8から導入されたラムダ式と匿名クラスの違いが分からず、使い分けに悩んでいる方

匿名クラス(無名クラス)とは?基本概念と通常クラスとの違い

Javaの匿名クラス(無名クラス)とは何かを示す図解

Javaの匿名クラス(無名クラス)とは、その名の通り名前を持たないクラスのことです。

通常のクラスのようにclass MyClass { ... }と名前を付けて定義するのではなく、インスタンスを生成するその場で、一度きりの使い捨てクラスとして定義します。

この「その場限り」という性質が、匿名クラスの最大の特徴であり、メリットでもあります。

匿名クラスが生まれた背景|Java 1.1で導入された理由

匿名クラスはJava 1.1(1997年)で、インナークラスの一種として導入されました。

当時のJavaはAWT/Swingを使ったGUIアプリケーションが主流で、ボタンクリックなどのイベントリスナーを大量に実装する必要がありました。

イベントごとに名前付きクラスを定義するのは冗長だったため、「その場で使い捨てのクラスを書ける」匿名クラスが重宝されたのです。

その後、Java 8(2014年)でラムダ式が登場し、関数型インターフェースの実装はラムダ式が主役になりました。

しかし、複数メソッドの実装やフィールド保持が必要な場面では、2026年現在も匿名クラスが使われ続けています。

匿名クラスに「名前がない」とはどういう意味か

クラスに名前がないとは、クラスを定義する際にclassキーワードの後ろにクラス名を記述しない、ということです。

例えば、MyRunnableという名前のクラスを作る場合、以下のように書きますよね。

// 名前付きの通常のクラス定義
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("スレッドを実行します。");
    }
}

// インスタンス化
Runnable runner = new MyRunnable();

一方、匿名クラスを使うと、MyRunnableという名前はどこにも登場しません。

// 匿名クラスを使ったインスタンス化
Runnable runner = new Runnable() {
    @Override
    public void run() {
        System.out.println("スレッドを実行します。");
    }
};

new Runnable()の直後に{}ブロックを続け、インターフェースのメソッドをその場で実装しています。

この{}で囲まれた部分が、名前のないクラスの定義そのものなのです。

匿名クラスと通常のクラスの違い|再利用性・コンストラクタ・実装

匿名クラスと通常のクラスの違いをまとめると、以下のようになります。

項目通常のクラス匿名クラス(無名クラス)
名前ありなし
再利用性何度でもインスタンス化できる一度しかインスタンス化できない
コンストラクタ独自のコンストラクタを定義できる独自のコンストラクタは定義できない
実装複数のインターフェースを実装できる一つのクラス継承か一つのインターフェース実装のみ
ファイルクラス名.javaとして独立したファイル他のクラスファイル内に定義される

最も大きな違いは再利用性です。

匿名クラスは、その場で定義してすぐに使う「インスタント」なクラスなので、別の場所で同じものを使いたくなった場合は、通常の名前付きクラスとして定義しなおす必要があります。

私の現場では「3回以上使うなら名前付きクラスに昇格させる」というルールを設けています。

匿名クラスのまま放置してコピペが増えたプロジェクトを何度も見てきたので、この基準は意外と有効です。

あわせて読む

匿名クラスを使うメリット|コードを簡潔に保てる3つの利点

匿名クラスを使うと、コードベース全体の見通しが良くなる実務的なメリットがあります。ここでは特に効果の大きい3つの利点を整理します。

  1. 定義とインスタンス化を同じ位置に書けるため、処理の流れが上から下へ一直線に追える
  2. 使い捨て用途でクラス数が増えないので、プロジェクトのファイルツリーが余計なクラスで膨らまない
  3. 「この場所専用」という意図を明示できるため、他のメンバーが誤って流用するリスクを減らせる

つまり匿名クラスは「処理を書きたい場所に、処理そのものを書ける」機能です。

クラスという器を介さずに振る舞いだけを差し替えられるため、イベント処理やコールバックのような「一品物のオブジェクト」を渡すシーンで威力を発揮します。

匿名クラスの使いどころ|イベント・スレッド・テストの3パターン

Javaの匿名クラスは、特定の場所で一度だけしか使わないような、ごく小規模な処理を実装する場合に非常に役立ちます。

主な用途は以下の3つです。

  1. イベントリスナーの実装GUIプログラミングで、ボタンがクリックされた時の処理などを記述する際によく使われる
  2. スレッドの処理内容の定義ThreadクラスやRunnableインターフェースを使って、新しいスレッドで実行したい処理を簡潔に書ける
  3. テスト用の簡易的なオブジェクト生成テストコードの中で、インターフェースを実装した一時的なオブジェクト(モックやスタブ)を作る場合に便利

いずれのケースも、「この処理はこの場所専用で、他では使わない」という共通点があります。

わざわざ新しいファイルを作ってクラスを定義するまでもない、ちょっとした処理を埋め込むのに最適なのが、この匿名クラスなのです。

Java匿名クラスの書き方|基本構文と4つの実装パターン

匿名クラスの書き方を示す図解

匿名クラスは new 親クラス名() { 実装 }; の構文で、インターフェース実装・抽象クラス継承・外部変数アクセス・独自メソッド追加の4パターンで使えます。

匿名クラスの概念が分かったところで、具体的な書き方を見ていきましょう。構文自体はシンプルなので、ルールさえ覚えてしまえばすぐに使いこなせます。

Java匿名クラスの基本構文と記述ルール

Javaの匿名クラスは、以下の構文で記述します。

new 親クラス名 or インターフェース名() {
    // フィールドの定義
    // メソッドのオーバーライドや追加
};

ポイントは、new演算子の後に、継承するクラス名か実装するインターフェース名を書き、その直後の()に続けて{}ブロックでクラスの中身を定義する点です。

文の終わりにはセミコロン;が必要な点に注意してください。これは、匿名クラスの定義全体が、一つの式として扱われるためです。

インターフェース実装時の匿名クラスの書き方

インターフェースを実装する際の匿名クラスは、最もよく見かけるパターンです。Runnableインターフェースを例に見てみましょう。

Runnableにはrun()というメソッドが一つだけ定義されています。

// Runnableインターフェースを実装する匿名クラス
Runnable runnable = new Runnable() {
    @Override
    public void run() {
        // ここにスレッドで実行したい処理を書く
        for (int i = 0; i < 3; i++) {
            System.out.println("匿名クラスで実行中...");
        }
    }
};

// スレッドを生成して実行
Thread thread = new Thread(runnable);
thread.start();

このコードでは、new Runnable() { ... }の部分でRunnableインターフェースを実装した名前のないクラスを定義し、同時にそのインスタンスを生成しています。

生成されたインスタンスはrunnable変数に代入され、Threadのコンストラクタに渡されています。

抽象クラス継承時の匿名クラスの書き方

抽象クラスを継承する場合も、インターフェースの場合とほとんど同じです。継承したクラスの抽象メソッドを、{}ブロックの中で実装(オーバーライド)します。

例えば、Animalという抽象クラスがあったとします。

// 鳴き声を出力する抽象メソッドを持つ抽象クラス
abstract class Animal {
    abstract void cry();
}

このAnimalクラスを継承する匿名クラスは、以下のように書けます。

// Animalクラスを継承する匿名クラス
Animal dog = new Animal() {
    @Override
    void cry() {
        System.out.println("ワン!");
    }
};

dog.cry(); // "ワン!" と出力される

Animal cat = new Animal() {
    @Override
    void cry() {
        System.out.println("ニャー");
    }
};

cat.cry(); // "ニャー" と出力される

その場ですぐに異なる実装を持つオブジェクトを手軽に作れるのが、匿名クラスの強みです。

あわせて読む

匿名クラスから外側の変数にアクセスする条件|実質的finalとは

匿名クラスは、外側のメソッド内で宣言されたローカル変数を参照できます。

ただし、参照される変数は実質的final(effectively final)でなければなりません。

「実質的final」とは、finalキーワードを明示していなくても、一度代入された後に再代入されていない変数のことです。

Java 8以降のコンパイラは、再代入が無ければ自動的にこの条件を満たしたとみなしてくれます。

public void example() {
    int count = 10; // 再代入しなければOK(effectively final)

    Runnable r = new Runnable() {
        @Override
        public void run() {
            System.out.println(count); // 外側のローカル変数を参照できる
        }
    };

    // count = 20; ← この行を有効にすると、上の匿名クラスが
    //               "local variables referenced from an inner class
    //                must be final or effectively final" エラーに
    r.run();
}

Java 7以前はfinalを必ず明示する必要がありましたが、Java 8以降は書き忘れてもコンパイラが判定してくれるようになりました。

どうしても外側の値をスレッド間で書き換えたい場合は、サイズ1の配列やAtomicIntegerでラップするなどの回避策を取ります。

匿名クラスの実践的な使用例3選|イベント・スレッド・ソート

匿名クラスの使用例(イベント・スレッド・Comparator)を示す図解

正直に言うと、2026年の実務で匿名クラスを新規に書く機会はかなり減りました。ラムダ式で済む場面がほとんどだからです。

ただし、以下の3パターンは今でもラムダ式では代替できないか、匿名クラスの方が読みやすいケースとして現役です。

匿名クラスでボタンイベント(ActionListener)を実装する例

GUIアプリケーションでボタンがクリックされたときの動作を定義するActionListenerは、匿名クラスが活躍する代表的な場面です。

javax.swingパッケージを使った簡単な例を示します。

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class ButtonExample {
    public static void main(String[] args) {
        JFrame frame = new JFrame("匿名クラスの例");
        JButton button = new JButton("クリックしてね");

        // ボタンがクリックされたときの処理を匿名クラスで実装
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(frame, "ボタンがクリックされました!");
            }
        });

        frame.getContentPane().add(button);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(300, 200);
        frame.setVisible(true);
    }
}

button.addActionListener()の引数に、ActionListenerインターフェースを実装した匿名クラスを直接渡しています。

このボタンのためだけの処理なので、わざわざ名前付きのクラスを作るよりもコードがシンプルにまとまりますね。

なお、javax.swingはJava SEのコアパッケージであり、Jakarta EE移行(javax.*jakarta.*)の対象外です。

そのまま使い続けて問題ありません。

匿名クラスでスレッド(Runnable)を実装する例

先ほども少し触れましたが、Runnableインターフェースを使ったスレッド処理も、Javaの無名クラスが頻繁に使われる場面です。

public class ThreadExample {
    public static void main(String[] args) {
        System.out.println("メインスレッドを開始します。");

        // Runnableを実装する匿名クラスをThreadのコンストラクタに直接渡す
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("別スレッドを開始します。");
                    Thread.sleep(2000); // 2秒待機
                    System.out.println("別スレッドを終了します。");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        thread.start(); // スレッドの実行を開始

        System.out.println("メインスレッドを終了します。");
    }
}

この例では、new Thread()の中に直接new Runnable() { ... }を記述しています。

これにより、一時的な処理のためだけにRunnableを実装したクラスを別途定義する手間が省け、コードが非常にコンパクトになります。

匿名クラスでComparatorを実装するソート例

例えば、ソート機能の順序をカスタマイズするためにComparatorインターフェースを使う場合も、匿名クラスが便利です。

Userクラスのリストを、年齢(age)でソートする例を見てください。

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

class User {
    String name;
    int age;
    User(String name, int age) { this.name = name; this.age = age; }
    @Override
    public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; }
}

public class ComparatorExample {
    public static void main(String[] args) {
        List<User> userList = new ArrayList<>();
        userList.add(new User("Taro", 30));
        userList.add(new User("Jiro", 25));
        userList.add(new User("Saburo", 35));

        // 年齢でソートするためのComparatorを匿名クラスで実装
        Collections.sort(userList, new Comparator<User>() {
            @Override
            public int compare(User u1, User u2) {
                return Integer.compare(u1.age, u2.age);
            }
        });

        System.out.println(userList);
        // 出力: [User{name='Jiro', age=25}, User{name='Taro', age=30}, User{name='Saburo', age=35}]
    }
}

Collections.sort()の第2引数に、Comparatorをその場で実装した匿名クラスを渡しています。

このソート順はこの場所でしか使わないため、匿名クラスが最適な選択肢となります。

匿名クラスにフィールドや独自メソッドを追加する書き方

匿名クラスは、インターフェースや抽象クラスをオーバーライドするだけでなく、独自のフィールドや補助メソッドを内部に追加できるという特徴があります。

これはラムダ式にはできない書き方です。

例えば、Comparatorに「比較回数をカウントするフィールド」を持たせたい場合、匿名クラスなら次のように書けます。

Comparator<User> ageComparator = new Comparator<User>() {
    // 匿名クラス内で独自フィールドを宣言
    private int compareCount = 0;

    @Override
    public int compare(User u1, User u2) {
        compareCount++;
        logCount();
        return Integer.compare(u1.age, u2.age);
    }

    // オーバーライド以外の独自メソッドも定義できる
    private void logCount() {
        System.out.println("compare呼び出し回数: " + compareCount);
    }
};

同等の処理をラムダ式で書こうとすると、関数型インターフェースは抽象メソッドが1つだけという制約があるため、状態を持つフィールドや補助メソッドを内部に抱え込むことができません。

「ちょっとした状態付きの一品物オブジェクト」が欲しいときは、匿名クラスが最適な選択肢になります。

匿名クラスを使うときの注意点|ネスト・デバッグ・リファクタリング

匿名クラス利用時の注意点を示す図解

匿名クラスの主な注意点は、ネストの深さ・デバッグの困難さ・再利用不可の3つで、いずれもリファクタリングで対処できます。

Javaの匿名クラスは便利な反面、注意すべき点もいくつか存在します。知らずに多用すると、後でコードの保守に苦労します。

匿名クラスはネストが深くなりやすい問題点

匿名クラスの処理の中に、さらに別の匿名クラスを記述する…といった入れ子(ネスト)構造も可能です。

しかし、これはコードの可読性を著しく低下させる原因になります。

// ネストが深くなった悪い例
someMethod(new SomeInterface() {
    @Override
    public void execute() {
        anotherMethod(new AnotherInterface() {
            @Override
            public void process() {
                // 処理がどんどん内側に入っていく…
                // インデントが深くなり、非常に読みにくい
            }
        });
    }
});

インデントが深くなりすぎたコードは、処理の流れを追うのが困難です。ネストが2段階以上になるようなら、ラムダ式やメソッドへの切り出しを検討すべきです。

匿名クラスはデバッグ・再利用が難しい

匿名クラスには名前がないため、デバッグ時に少し困ることがあります。

例外が発生したときのスタックトレースには、OuterClassName$1のように「外側のクラス名$数字」という形で表示されます。

どの匿名クラスで問題が起きたのか、特定しにくい場合があるのです。

また、匿名クラスは定義したその場でしか使えません。

もし複数の場所で同じような処理が必要になった場合は、コピペでコードを増やすのではなく、きちんと名前の付いた通常のクラスとして再定義(リファクタリング)しましょう。

デバッグのTipsとして、例外スタックトレースに表示される $1 $2 の番号は、ソースファイル内の匿名クラスの出現順に対応しています。

IDEの「Go to Line」機能でスタックトレースの行番号にジャンプすれば、どの匿名クラスかはすぐに特定できます。

匿名クラスをリファクタリングするときの対処法

もし、あなたがメンテナンスしているコードに、肥大化してしまった複雑な匿名クラスがあったら、以下の対処法が有効です。

その場合の対処法は「クラスの抽出」というリファクタリング手法です。

  1. 匿名クラスで行っている処理を、新しい独立したクラスとして定義します。
  2. 元のコードでは、その新しいクラスのインスタンスを生成するように書き換えます。

これにより、処理に適切な名前が与えられ、単体でのテストも容易になります。コードの見通しが良くなり、再利用性も高まります。

IDE(統合開発環境)の多くは、このリファクタリングを自動で行う機能を持っています。

あわせて読む

匿名クラスとラムダ式の違い|どちらを使うべきか判断基準

匿名クラスとラムダ式の違いを示す図解

Java 8でラムダ式が登場したことで、匿名クラスの使われ方は大きく変わりました。

ラムダ式は、特定の条件下で匿名クラスをよりシンプルに書くためのものです。

匿名クラスとラムダ式の関係(Java 8以降の進化)

ラムダ式は、実装すべきメソッドが一つだけのインターフェース(関数型インターフェース)の実装を、非常に簡潔に記述できる構文です。

先ほどのRunnableの例を思い出してください。

// 匿名クラスの場合
Runnable runnable1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("実行します。");
    }
};

// ラムダ式の場合
Runnable runnable2 = () -> System.out.println("実行します。");

() -> System.out.println("実行します。")の部分がラムダ式です。

メソッド名や@Overrideアノテーションなどがなくなり、引数リスト()と処理内容{}(1行の場合は省略可)だけで記述できています。

これは、Runnablerun()というメソッドを一つしか持たない関数型インターフェースだからこそ可能な記法です。

コンパイラが文脈から判断して、このラムダ式がRunnableインターフェースのrun()メソッドの実装であると解釈してくれます。

あわせて読む

匿名クラスとラムダ式はどちらを使うべきか|判断基準

匿名クラスとラムダ式のどちらを使うべきか、判断基準はとてもシンプルです。

  • ラムダ式を使うべき場合:
    • 関数型インターフェース(抽象メソッドが1つだけのインターフェース)を実装するとき。
    • ActionListener, Runnable, Comparatorなど、多くのケースが該当します。
  • 匿名クラスを使うべき場合:
    • 実装(オーバーライド)すべきメソッドが複数あるインターフェースや抽象クラスを扱うとき。
    • クラス内でフィールド(メンバ変数)や、オーバーライド以外の追加メソッドを定義したいとき。
    • thisキーワードが、匿名クラスのインスタンス自身を指す必要があるとき。(ラムダ式の場合、thisは外側のクラスのインスタンスを指します)

基本的には「ラムダ式で書けるなら、ラムダ式を使う」のが現代のJavaプログラミングの主流です。その方がコードが短く、すっきりするためです。

匿名クラスとラムダ式の可読性・メンテナンス性を比較

シンプルな処理であれば、ラムダ式の方が圧倒的に可読性が高くなります。

// 匿名クラス
button.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("クリック!");
    }
});

// ラムダ式
button.addActionListener(e -> System.out.println("クリック!"));

一目瞭然ですね。ラムダ式は処理の本質だけを抜き出したような記述が可能です。

一方で、処理が複雑になってくると話は変わります。ラムダ式の中に何十行ものコードを書くと、かえって読みにくくなる場合があります。

そのような場合は、匿名クラスを使うか、あるいは処理をメソッドとして切り出してメソッド参照(例: this::handleClick)を使う方が、メンテナンス性は向上します。

Java匿名クラスに関するよくある質問|使いどころ・ラムダ式との違いQ&A

最後に、Javaの匿名クラスについて初学者から頻繁に寄せられる質問をまとめておきます。

Q. 匿名クラスとラムダ式はどちらを優先して使うべき?

関数型インターフェース(抽象メソッドが1つだけ)を実装するだけならラムダ式を優先します。複数メソッドの実装やフィールドが必要になるケースでのみ匿名クラスを選びましょう。

Q. 匿名クラスで複数のインターフェースを実装できる?

できません。匿名クラスは「1つのクラスの継承」または「1つのインターフェースの実装」のいずれかしか選べません。複数インターフェースを束ねたい場合は、通常の名前付きクラスで実装します。

Q. 匿名クラスにコンストラクタは定義できる?

独自のコンストラクタは定義できません。匿名クラスには名前が無く、コンストラクタは「クラス名と同じメソッド」として宣言する必要があるためです。初期化処理を行いたい場合はインスタンス初期化子ブロック({ ... })を使います。

Q. スタックトレースで$1と表示されるのはなぜ?

コンパイラが匿名クラスに自動的に割り振る名前です。1つ目の匿名クラスは外側クラス名$1、2つ目は$2…と連番で付与されます。デバッグ時は、スタックトレースの行番号と突き合わせて該当箇所を特定してください。

Q. 匿名クラスはメモリリークの原因になる?

可能性があります。非静的な匿名クラスは外側のインスタンスへの暗黙参照を持つため、匿名クラスのインスタンスが長生きすると外側オブジェクトを解放できません。AndroidのActivity等、ライフサイクルが限定されるクラスで長時間保持される匿名クラスを作るときは特に注意が必要です。

まとめ:匿名クラスは「一度きりの即席オブジェクト」

Javaの匿名クラス(無名クラス)は、特定の場所で一度だけ使用するオブジェクトを、その場で手軽に定義するための機能です。

イベントリスナーやスレッド処理など、「使い捨て」のロジックを実装する際にコードを簡潔に保つ効果があります。

匿名クラスを理解するとラムダ式も理解しやすい

Java 8から導入されたラムダ式は、この匿名クラスの考え方がベースにあります。

匿名クラスが「メソッドを実装した、名前のないオブジェクト」であるのに対し、ラムダ式はそこからさらに定型的な記述を省略した「処理そのもの」を記述する仕組みです。

なお、2026年時点の最新LTSはJava 25ですが、匿名クラスの構文や動作はJava 1.1から変わっておらず、どのバージョンでもそのまま使えます。

そのため、匿名クラスの概念をきちんと理解しておくことが、ラムダ式をより深く、正しく使いこなすための近道になります。

匿名クラスはシンプルな実装と明確な責務を意識しよう

匿名クラスは強力ですが、乱用は禁物です。処理が数行に収まるようなシンプルなケースに限定して使用するのがベストです。

もし匿名クラスのコードが長くなってきたら、それはクラスとして独立させるべきサインです。

  • この記事を書いた人
  • 最新記事

トム

・都内自社開発企業勤務/Javaバックエンドエンジニア
/Java歴10年以上 ・首都圏在住30代
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎 詳細なプロフィール

-Java入門