優しい夜
第8章 インナークラス(SJC-P 1.4)
2007-05-13-Sun  CATEGORY: Java
≫目次
■「通常」のインナークラス
■メソッドローカルなインナークラス
■無名インナークラス
■静的ネストクラス(トップレベルネストクラス,静的インナークラス)


インナークラスのインスタンスは、アウタークラスの全てのメンバーにアクセス可能(privateとして宣言されているメンバーも例外ではない)。静的ネストクラスは除く。

■「通常」のインナークラス
位置はメソッドと並列。
「通常」のインナークラスは、static宣言をいっさい持つことが出来ない。

□インナークラスへのアクセス
1)アウタークラスのコード内の非静的メソッドからインナークラスへアクセス。
<呼び出し方はノーマル>
class MyOuter{
	private int x=7;
	public void makeInner(){
		MyInner in = new MyInner();
		//MyOuter.MyInner in = new MyOuter.MyInner();でも可
		//さらに2)と同じ呼び出し方も可能。
		in.seeOuter();
	}
	class MyInner{ //「通常」のインナークラス
		public void seeOuter(){
			System.out.println("Outer x is "+x);
		}
	}
}
2)アウタークラスの静的メソッドやアウタークラスの外のクラスからのアクセス。
アクセスにアウタークラスのインスタンスも必要。
<2行表記>
MyOuter mo = new MyOuter();
MyOuter.MyInner inner = mo.new MyInner();
//左辺のMyOuter.はなくても可
inner.seeOuter();

<1行表記>
MyOuter.MyInner inner =new MyOuter().new MyInner();
//左辺のMyOuter.はなくても可
inner.seeOuter();
□インナークラス内でインナークラス(自分)のインスタンスの参照
<this>
thisは非静的メソッド(インスタンスコード)内でしか使えない。
thisは現在実行中のメソッドを呼び出す際に使用したオブジェクト参照を返す。

□インナークラス内でアウタークラスのインスタンスの参照
<MyOuter.this>←アウタークラス名.this
アウタークラスの参照を他のコードに渡す時に使われる。

□修飾子
「通常」のインナークラスは、インスタンス変数やインスタンスメソッドと同じくアウタークラスのメンバーになるので、同様のメンバー修飾子が使用可能。
普通のクラスと比べて、アクセス修飾子「protected, private」が使えるのが特殊。

使用可能な修飾子:public, protected, private, final, abstract, strictfp, static(静的ネストクラス)
(メソッド専用のsynchronized, nativeとインスタンス変数専用のtransientは除く)

インナークラスをabstractとして定義した場合、その抽象インナークラスの具象サブクラスだけをインスタンス化することが出来る。(抽象インナークラス自体はインスタンス化出来なくなるので、無名インナークラス(無名具象サブクラス)を利用することになる。)

■メソッドローカルなインナークラス
メソッド内に定義されたインナークラス。
メソッドローカルなインナークラスを利用するには、そのメソッドローカルなインナークラスの定義より後の部分のどこかで、インスタンスを作成する。
void doStuff(){ //非静的メソッド
    class MyInner{ //メソッドローカルなインナークラス
        public void seeOuter(){
            System.out.println("Outer x is "+x);
        }
    }
    MyInner mi = new MyInner(); //必ずインナークラスの定義の後でインスタンス化
    mi.seeOuter();
}
□メソッドローカルなインナークラスにできること
インスタンス化は、それが定義されているメソッドの中でのみ可能。
「通常」のインナークラスと同様、アウタークラスの全てのメンバーにアクセス可能。

※静的メソッド内に宣言されたインナークラスは、それを内包しているクラスの静的メンバーにしかアクセスできない。(静的メソッドと同じ制限)

□メソッドローカルなインナークラスにできないこと
すぐ外側のメソッド(そのインナークラスが定義されているメソッド)のローカル変数(引数も含む)を使用できない(メソッドが終了しローカル変数が消滅した後も、インナークラスのオブジェクトはヒープ上に残る可能性がある為)。
ただし、そのローカル変数がfinalとして宣言されている場合は、使用可能。

□修飾子
ローカル変数とほぼ同じ扱い。
final, abstractのみ使用可。
(メソッドローカルなインナークラスを抽象クラスとして宣言してもあまり意味はない)

■無名インナークラス
宣言時にクラス名を指定しないインナークラス。
メソッド内でも、メソッドの引数内でも定義可能。
他のクラスのメソッドをオーバーライドしたり、インタフェースの抽象メソッドを実装したり、抽象クラスの抽象メソッドを実装したりするのに使われる。

無名インナークラスでは多態性が作用する。親型の変数を使って親にないメソッドは呼び出せない(呼び出すとコンパイルエラー)。
無名インナークラスのインスタンスに対して呼び出せるメソッドは、その「外側」に当たる名前付きクラスやインタフェースの中で定義されているメソッドだけ。
無名インナークラス内で新たなメソッドを定義しても、そのメソッドを無名インナークラスの外から呼び出すことは出来ない。

□単純な無名インナークラス(サブクラス)
Popcorn型の参照変数pは、Popcornのインスタンスではなく、「Popcornの無名サブクラス」のインスタンスを参照している。
親クラスのインスタンスを作成しているように見えるかも知れないが、実は(無名)サブクラスのインスタンスを作成しており、同時に親クラスが持つメソッドをオーバーライドしている。
class Popcorn{
    public void pop(){
        System.out.println("popcorn");
    }
}
class Food{
    Popcorn p = new Popcorn(){ //Popcornクラスの無名サブクラスの定義開始
        public void pop(){ //オーバーライド
            System.out.println("anonymous popcorn");
        }
    }; //無名サブクラスの定義終了
}
※単純な無名インナークラスの定義終了の記述「};」に注意。
※中括弧の後にセミコロンという形は、無名インナークラスと配列の初期化の省略構文しかない。

□単純な無名インナークラス(インタフェース)
Cookable型の参照変数cは、Cookableインタフェースのインスタンスではなく、「Cookableインタフェースの無名実装クラス」のインスタンスを参照している。
(そもそもインタフェースは、どんな場合でもインスタンス化できない)
インタフェースのインスタンスを作成しているように見えるかも知れないが、実は(無名)実装クラスのインスタンスを作成しており、同時にインタフェースが持つ抽象メソッドを実装(具象化)している。
interface Cookable{
    public void cook();
}
class Food{
    Cookable c = new Cookable(){ //Cookableインタフェースの無名実装クラスの定義開始
        public void cook(){ //実装(具象化)
            System.out.println("anonymous cookable implements");
        }
    }; //無名実装クラスの定義終了
}
※単純な無名インナークラスの定義終了の記述「};」に注意。
※インタフェースのインスタンスは作成できないので、new Cookable()という構文は無名実装クラスを扱う時にしか出てこない。
※無名インナークラスには、複数のインタフェースを実装する仕組み(「,」)がない。
※無名インナークラスでは、1つのインタフェースしか実装できない。
※クラスの継承とインタフェースの実装を同時に行うこともできない。

□引数扱いの無名インナークラス
引数の中でクラスを定義する。

下記では、引数内で、インタフェースの無名実装クラスを定義し、そのインスタンスを作成しつつ、インタフェースの抽象メソッドを実装(具象化)している。
class MyWonderfulClass{
    void go(){
        Bar b = new Bar();
        b.doStuff(
            new Foo(){ //Fooインタフェースの無名実装クラスの定義開始
                public void foof(){ //実装(具象化)
                    System.out.println("foofy");
                }
            } //無名実装クラスの定義終了
        );
    }
}
interface Foo{
    void foof(); //暗黙でpublic abstract
}
class Bar{
    void doStuff(Foo f){}
}
※引数扱いの無名インナークラスの定義終了の記述「});」に注意。

■静的ネストクラス(トップレベルネストクラス,静的インナークラス)
静的ネストクラスは、アウタークラスのただの「静的メンバー」にすぎない。(静的クラスではない。静的クラスなどと言うものは存在しない。)
静的メソッドと同様に、静的ネストクラスも、アウタークラスのメンバーへ直接のアクセスはできない。

□静的ネストクラスへのアクセス
静的メンバーなのでアウタークラスのインスタンスは不要。
記述例1:
class BigOuter{
	static class Nested{} //静的ネストクラス
}
class Boom{
	public static void main(String[] args){ //静的メソッド
		BigOuter.Nested n = new BigOuter.Nested();
	}
	void meth(int x){ //非静的メソッド
		BigOuter.Nested n = new BigOuter.Nested();
	}
}

記述例2:
class BigOuter{
	static class Nested{} //静的ネストクラス
	public static void main(String[] args){ //静的メソッド
		Nested n = new Nested();
		//同じクラス内なので外側のクラス名は省略可。付けても可。
	}
	void meth(int x){ //非静的メソッド
		Nested n = new Nested();
		//同じクラス内なので外側のクラス名は省略可。付けても可。
	}
}
Trackbacks0 Comments0
Comments

Only the blog author may view the comment.
 
Trackbacks
TB*URL
ページトップへ
Copyright © 2009 優しい夜. all rights reserved.