読者です 読者をやめる 読者になる 読者になる

Javaにおける4種類のインナークラス

Javaはクラス内にクラスを定義することが出来ます。クラス内に定義されたクラスはインナークラス(内部クラス)と呼ばれており、4種類の定義方法が存在します。定義方法によって異なった特性を持っており、間違った使い方をすると思わぬバグの元になり得ます。

本記事では4種類のインナークラスの違いのみを扱います。インナークラスの用途等は扱いません。今後取り上げるかもしれませんが。

本記事では、エンクロージングクラス = インナークラスからみて外側にあるクラスとします。

Javaにおける4種類のインナークラス

  • 非staticインナークラス
  • staticインナークラス
  • ローカルクラス
  • 無名クラス

非staticインナークラス

普通に定義したインナークラスです。

public class OuterClass {
    public static void main(String[] args) {
        new OuterClass();
    }

    private String className = OuterClass.class.getSimpleName();
    public OuterClass() {
        new InnerClass();
    }

    private class InnerClass {
        public InnerClass() {
            System.out.println(className); // OuterClass
        }
    }
}

非staticインナークラスはエンクロージングクラスへの暗黙的な参照を持ちます。その証拠として上記のコードのようにインナークラス内からエンクロージングクラスのインスタンス変数にアクセス可能です。この特徴は便利なように聞こえるかもしれません。確かに便利ではあるのですが、暗黙的な参照があるということはメモリリークの原因になることがあります。さらに言うと、暗黙的な参照はコード上に現れないため原因を特定するのが非常に困難である場合があります。

staticインナークラス

インナークラスの修飾子にstaticを付けるとstaticインナークラスとなります。

public class OuterClass {
    public static void main(String[] args) {
        new OuterClass();
    }

    private String className = OuterClass.class.getSimpleName();
    public OuterClass() {
        new InnerClass();
    }

    private static class InnerClass {
        public InnerClass() {
            System.out.println(className); // コンパイルエラー
        }
    }
}

staticインナークラスはエンクロージングクラスへの参照を持ちません。上記のコードで示したように、インナークラス内からエンクロージングクラスのインスタンス変数にアクセスすることは出来ません。

また、staticインナークラスはトップレベルに定義したクラスとほぼ同様に扱うことが出来ます。

ローカルクラス

メソッド内で定義するクラスです。正直あまり使ったことがありません。ローカルクラスを使うとクラスのスコープを狭めることが出来るため、バグを減らすテクニックとして使われるようです。

public class OuterClass {
    public static void main(String[] args) {
        class LocalClass {
            private String className = LocalClass.class.getSimpleName();
            public LocalClass() {
                System.out.println(className); // LocalClass
            }
        }
        new LocalClass();
    }
}

無名クラス

クラスの実装とインスタンス化を同時に行う手法です。

public interface OnClickListener {
    public void onClick();
}
public class OuterClass {
    private OnClickListener listener = new OnClickListener() {
        @Override
        public void onClick() {}
    };
}

まとめ

上記で説明したように非staticインナークラスはエンクロージングクラスへの暗黙的な参照を持ち、思わぬバグの原因となることがあります。Effective Javaでも言及されていますが、基本的にはインナークラスを作る際は非staticインナークラスではなく、staticインナークラスとして作る方が良さそうです。