匿名クラス:使い捨ての便利屋

docs

前回の記事では、インターフェースを使ってクラスの「型」を定義する方法を学んだよね。インターフェースを使えば、複数のクラスに共通の振る舞いを強制したり、疎結合なコードを書いたりできるんだった。

インターフェースの基本:契約を定義する | ToolDocs

今回は、そんなインターフェースや抽象クラスと相性抜群の、**「匿名クラス」**について深掘りしていくよ。名前からしてちょっと怪しげだけど、これがなかなかどうして、めちゃくちゃ便利なやつなんだ。

匿名クラスってなんだ?

まず、匿名クラスを一言で説明するなら、**「名前のない、一時的に使うためだけのクラス」って感じかな。通常のクラスは、class MyClass { ... }みたいに名前をつけて定義するよね。でも匿名クラスは、その場でサッと作って、一度使ったらおしまい!っていう、まさに「使い捨ての便利屋」**なんだ。

どんな時に使うかというと、

  • インターフェースを実装するクラスを一時的に作りたい時
  • 抽象クラスを継承するクラスを一時的に作りたい時
  • イベントリスナーみたいに、特定の処理をその場で記述したい時

なんかに威力を発揮するよ。

匿名クラスの書き方

匿名クラスの基本的な書き方はこんな感じ。ちょっと特殊だから、じっくり見てみてね。

Java

// インターフェースの場合
InterfaceName instance = new InterfaceName() {
    // インターフェースのメソッドを実装する
    @Override
    public void someMethod() {
        // 処理
    }
};

// 抽象クラスの場合
AbstractClassName instance = new AbstractClassName() {
    // 抽象メソッドを実装する
    @Override
    public void someAbstractMethod() {
        // 処理
    }

    // 必要であれば、他のメソッドもオーバーライドできる
};

ポイントは、new InterfaceName() { ... }とかnew AbstractClassName() { ... }って部分。普通のインスタンス化に見えるけど、その後に{ ... }が続いているのが匿名クラスの目印だよ。この波括弧の中に、インターフェースのメソッドを実装したり、抽象クラスの抽象メソッドをオーバーライドしたりするんだ。

例を見てみよう!

言葉だけだと分かりにくいから、具体的な例で匿名クラスの便利さを体験してみよう。

例1:シンプルな挨拶インターフェース

まずは、前回のインターフェースの話でも出てきたような簡単な例から。挨拶をするインターフェースを作ってみよう。

Java

// インターフェースの定義
interface Greeter {
    void sayHello();
}

public class AnonymousClassExample1 {
    public static void main(String[] args) {
        // 匿名クラスを使ってGreeterインターフェースを実装
        Greeter englishGreeter = new Greeter() {
            @Override
            public void sayHello() {
                System.out.println("Hello!");
            }
        };

        Greeter japaneseGreeter = new Greeter() {
            @Override
            public void sayHello() {
                System.out.println("こんにちは!");
            }
        };

        englishGreeter.sayHello();   // 出力: Hello!
        japaneseGreeter.sayHello();  // 出力: こんにちは!
    }
}

この例では、Greeterインターフェースを実装するクラスを、わざわざEnglishGreeterとかJapaneseGreeterみたいに名前をつけて作らなくても、その場でnew Greeter() { ... }という形で匿名クラスとして実装しているね。これは、**「この処理のためだけにちょっとだけGreeterを実装したいんだ!」**っていう時にすごく便利なんだ。

vscodeで実行した結果

もし匿名クラスを使わないなら、こんな感じでクラスを定義する必要があるよね。

Java

class EnglishGreeter implements Greeter {
    @Override
    public void sayHello() {
        System.out.println("Hello!");
    }
}

class JapaneseGreeter implements Greeter {
    @Override
    public void sayHello() {
        System.out.println("こんにちは!");
    }
}

public class AnonymousClassExample1_NoAnonymous {
    public static void main(String[] args) {
        Greeter englishGreeter = new EnglishGreeter();
        Greeter japaneseGreeter = new JapaneseGreeter();

        englishGreeter.sayHello();
        japaneseGreeter.sayHello();
    }
}

「英語の挨拶」と「日本語の挨拶」みたいに、別の振る舞いをさせたいけど、それぞれを独立したクラスとして定義するほどでもないな…って時に匿名クラスは輝くんだ。

vscodeで実行した結果

例2:ボタンのクリックイベント

匿名クラスがよく使われる場面の一つに、GUI(グラフィカルユーザーインターフェース)のイベント処理があるよ。例えば、ボタンがクリックされた時の処理なんかは、まさに匿名クラスの出番だ。

JavaFXやSwingといったGUIライブラリでは、ボタンにアクションを設定するために、特定のインターフェース(例えばActionListener)を実装したオブジェクトを渡すことが多いんだ。

Java

// 仮のGUIライブラリのイメージ (実際のJavaFXやSwingとは少し異なりますが、概念は同じです)
interface ActionListener {
    void actionPerformed();
}

class Button {
    private String label;
    private ActionListener listener;

    public Button(String label) {
        this.label = label;
    }

    public void setActionListener(ActionListener listener) {
        this.listener = listener;
    }

    public void click() {
        System.out.println(label + "ボタンがクリックされました!");
        if (listener != null) {
            listener.actionPerformed();
        }
    }
}

public class AnonymousClassExample2 {
    public static void main(String[] args) {
        Button clickMeButton = new Button("Click Me");

        // ボタンがクリックされた時の処理を匿名クラスで定義
        clickMeButton.setActionListener(new ActionListener() {
            @Override
            public void actionPerformed() {
                System.out.println("やった!ボタンが押されたよ!");
            }
        });

        Button exitButton = new Button("Exit");
        exitButton.setActionListener(new ActionListener() {
            @Override
            public void actionPerformed() {
                System.out.println("アプリケーションを終了します...");
                // 実際にはSystem.exit(0)などを行う
            }
        });

        // ボタンをクリックしてみる
        clickMeButton.click();
        exitButton.click();
    }
}

この例では、setActionListenerメソッドに、ActionListenerインターフェースを実装した匿名クラスを渡しているね。これによって、ボタンがクリックされた時に実行される処理を、その場でシンプルに書くことができるんだ。

もし匿名クラスを使わないとすると、ボタンごとに別々のクラスを作らないといけなくなって、コードがごちゃごちゃしちゃう可能性があるよね。

vscodeで実行した結果

例3:スレッドの実行処理

Javaで並行処理を行う「スレッド」を使う際にも、匿名クラスはよく登場するよ。Runnableインターフェースというものがあって、これを実装したクラスのrunメソッドに、スレッドで実行したい処理を書くんだ。

Java

public class AnonymousClassExample3 {
    public static void main(String[] args) {
        System.out.println("メインスレッド開始");

        // 新しいスレッドで実行する処理を匿名クラスで定義
        Thread myThread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println("サブスレッド: " + i);
                    try {
                        Thread.sleep(100); // 100ミリ秒待つ
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        myThread.start(); // スレッドを開始

        for (int i = 0; i < 5; i++) {
            System.out.println("メインスレッド: " + i);
            try {
                Thread.sleep(150); // 150ミリ秒待つ
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("メインスレッド終了");
    }
}

このコードを実行すると、「メインスレッド」と「サブスレッド」のメッセージが交互に出力されるはずだよ。ThreadクラスのコンストラクタにRunnableインターフェースを実装した匿名クラスを渡すことで、スレッドで実行したい処理をシンプルに定義できるんだ。

vscodeで実行した結果

匿名クラスの注意点

便利な匿名クラスだけど、いくつか注意点もあるよ。

  • コンストラクタを持てない: 匿名クラスは名前がないから、コンストラクタを明示的に定義することはできないんだ。初期化が必要な場合は、フィールド初期化子やインスタンス初期化ブロックを使うことになるよ。
  • 一度しかインスタンス化しない: 基本的に、匿名クラスは一度きりの使い捨て。何度も同じ処理を使い回したい場合は、通常のクラスとして定義する方がいい。
  • 短いコードに使う: あまりに処理が複雑になったり、メソッドが多かったりすると、匿名クラスの可読性が落ちてしまう。シンプルで短い処理に限定して使うのがおすすめだよ。
  • 外部のローカル変数にアクセスする場合: 匿名クラスの中から、それを囲むメソッドのローカル変数にアクセスする場合、そのローカル変数は実質的にfinalである必要があるよ(Java 8以降は明示的にfinalと書かなくても暗黙的にfinal扱いになることが多いけど、変更できない変数と覚えておくといい)。

まとめ

匿名クラスは、**「一時的にインターフェースを実装したり、抽象クラスを継承したりして、その場限りの処理を書きたい」**という時にめちゃくちゃ役立つ機能だ。特にイベント処理やスレッドなど、特定の振る舞いを簡潔に定義したい場面で真価を発揮するよ。

初めは少し特殊な書き方で戸惑うかもしれないけど、例を参考に何度も書いてみて慣れていこう! コードの量も減らせるし、可読性もアップするから、ぜひ使いこなしてみてね。


次回の記事も、Javaの便利な機能を紹介していくから楽しみにしててね!

前回の記事:インターフェースの基本:契約を定義する | ToolDocs

コメント

タイトルとURLをコピーしました