ネストした型-内部クラス(インナークラス)(1)
内部クラスは、クラスの内部にネストしてクラスを定義する機能である。
クラスの内部にクラスを定義することにより、クラス間の関係を、クラスの継承関係とはまた別に、従属関係,親子関係で表現する事ができる。
また、内部クラスにprivate修飾子を付ける事により、内部クラスの外側のクラス(アウタークラス)からしかアクセスできないようにクラスを隠蔽するこもできる。
javaの内部クラスは、C#と違ってやや複雑だ。
内部クラスにはクラスのメンバーとして宣言される内部クラスと、ローカル(内部)クラスと呼ばれるメソッド内で宣言される内部クラスがある。
さらに、クラスのメンバーとして宣言される内部クラスには、クラスのインスタンスメンバーとして宣言されるものと、クラスの静的メンバーとして宣言されるものとがある。
インスタンスメンバーとして宣言された内部クラス
インスタンスメンバーとして宣言された内部クラスの例を示す。
リスト1(InnerClass.java)
インスタンスメンバーとして宣言された内部クラスは、その外側にあるクラス(外部クラス)のインスタンスをとおして、アクセスする事になる。
リスト1の内部クラスをインスタンス化する例を、以下に示す。
リスト2(MainClass.java)
インスタンスメンバーとして宣言された内部クラスをインスタンス化するためには、2段階の処理が必要になる。
まず、外側の外部クラスをインスタンス化して(6行目)、そのインスタンスのnew演算子を使って内部クラスをインスタンス化する(7行目)。
eclipseによってコンパイルされた結果のデレクトリのファイルをエクスプローラで参照してみると、以下のようにOuterClass$InnerClass.classという$の付いたクラスがみえる。
クラスのコンパイル結果は、「外部クラス名+$+内部クラス名.class」となる事がわかる。
クラスの静的メンバーとして宣言された内部クラス
クラスの静的メンバーとして内部クラスを宣言するには、通常の静的メンバーと同様、class宣言の前にstaticを付ける。
以下にその例を示す。
リスト3(OuterClass2.java)
リスト1のコードの内部クラスのclass宣言の前にstatic修飾子を付けただけである。
次に、クラスの静的メンバーとして宣言された内部クラスをインスタンス化する例を示す。
リスト4(MainClass2.java)
静的メンバーとして宣言された内部クラスは、外部クラスのインスタンスを生成しなくてもよい事がわかる。
これは、静的メンバーはインスタンスではなくクラスのメンバーであるので、当然と言えば当然であるが。
クラスの静的メンバーとして宣言された内部クラスは、トップレベルネストクラスと言うらしい。
ローカルクラス
{ }で囲まれたブロック内に、クラスを定義する事もできる。
{ }で囲まれたブロック内に定義されたクラスは、ローカルクラスと呼ばれる内部クラスであり、そのブロック内のみで参照可能なクラスとなる。
メソッドも{ }で囲まれており、この事は、メソッド内にクラスを定義できる事を意味する。
メソッド内で定義された内部クラスは、匿名クラス(匿名クラス(無名クラス)とイベントリスナー - 愚鈍人を参照)としてswingやandroidなどのGUIのイベントリスナーの処理によく使われる。
以下に、ローカルクラスの例を示す。
リスト5(LocalClass.java)
リスト5には3つローカルクラスInnerClassが定義されている。
最初に定義されているInnerClass(7行目)は、初期化子(を参照)内で定義されており、初期化ブロック内(5~13行目)のみで有効である。
2番目に定義されているInnerClass(21行目)は、その外側の{}ブロック内(19~29行目)のみで有効である。
3番目に定義されているInnerClass(31行目)は、メソッド内(17~39行目)でのみで有効となる。
同じInnerClassという名前のクラスが複数定義されているが、それぞれ個々のブロック内のみで有効であり、別々のクラスとして認識される事になる。
ローカルクラスはクラスのメンバーでは無いので、継承とは無関係である。
なので、アクセス修飾子public,praivate,protectedを指定しても意味がないので、アクセス修飾子を付ける事ができない。
内部クラスと静的メンバー
インスタンスメンバーとして宣言された内部クラスとローカルクラスは、通常のクラスのようにstaticメンバーを定義する事はできないようだ。
但し、fainalなstaticメンバーは許される。
リスト1-1(OuterClass.java)
内部クラスと内部インターフェース
クラスの内にインテーフェースを定義したり、逆にインターフェース内にクラスを定義する事もできる。
内部インターフェースは、staticを付けなくても常にstaticになる。
内部インターフェースの例を以下に示す。
リスト6(InnerInterfaceSample.java)
インターフェース内に内部クラスを定義する例についも、ついでにあげておく。
リスト7(InnerInterfaceWithOuterClessSample.java)
内部インターフェースはstaticを付けなくても常にstaticになる理由は、 インターフェースは、他のクラスにimplementsする事ではじめてインスタンス化が可能であって、そのままではインスタンス化できないので、 インターフェースをクラスのインスタンスメンバーとして宣言しても無意味なためと考えられる。
仮に、インスタンスメンバーとしてインターフェースを定義できてしまうと、以下のような奇妙なコードが実行できてしまう事になる。
リスト8-以下のようなコードを記述する事はできない
C#との比較
C#の内部クラスには、インスタンスメンバー,クラスの静的メンバーの区別はなく、常に、javaで言うクラスの静的メンバーとなるのでstatic修飾子はつけない。
C#では、クラスの宣言の前にstaticを付けるとstaticメンバーのみで構成されるクラス(staticクラス(静的クラス)と静的コンストラクタを参照)を意味し、javaとは別の意味を持つ。
C#は、メソッド内部にクラスを定義する事ができない。(ローカルクラスは無い)
C#の内部クラスについては入れ子にされた型-内部クラス - 愚鈍人を参照。
参考URL