WisdomSoft - for your serial experiences.

6.4 ローカル・クラス

メソッドのブロック内で宣言されるローカル・クラスを紹介します。ローカル・クラスのスコープはメソッド内に限定されるため、非公開でその場限りのインタフェースや抽象クラスの実装に応用できます。

6.4.1 ブロック内のクラス

驚くべきことに Java 言語ではメソッド内のブロックで新しいクラスを宣言することができます。これをローカル・クラスと呼びます。これは、オブジェクト指向とクラスの性質から考えると、やや例外的な処置となります。本来、クラスは再利用と汎用性を確保し、プログラムをモジュール化するためのテンプレートのはずです。それが、限定的な利用を確実とするローカルな領域に記述するのは異常事態といえるでしょう。しかし、筆者の経験上ローカル・クラスが役に立つことも実際にはあります。

ローカル・クラスはブロック内で宣言されるため、変数と同様のスコープを持ちます。そのブロックの外に出れば、ローカル・クラスは見えなくなります。つまり、ローカル・クラスを利用できるのはそれを宣言したブロック内のみということになります。ローカル・クラスが限定的な利用を確実とするというのは、そういう意味です。ローカル・クラスはクラスの内部で記述されるため、内部クラスとみなされます。

ローカル・クラス宣言では、トップレベルやメンバ・クラス宣言とは異なり、public、protected、private、static いずれの修飾子も指定することができません。また、ローカル・クラスのスコープは常にそのブロック内だけなので、結果的にローカル・クラスには限定名が存在しません。常に単純名でアクセスすることになるでしょう。こうした、可視範囲や修飾子の束縛を除けば、基本的にメンバ・クラスと同じです。ただし、静的コンテキスト内で宣言されたローカル・クラスは囲んでいるクラスのインスタンスとは関連付けられません。

コード1
class Test {
	private String str = "";
	public static void main(String args[]) {
		Test obj = new Test();
		obj.str = "Kitty on your lap";
		System.out.println(obj.getLocal());
	}

	public Object getLocal() {
		class Local {
			public String toString() {
				return super.toString() + " : " + str;
			}
		}
		return new Local();
	}
}
実行結果
>java Test
Test$1$Local@c78e57 : Kitty on your lap

コード1の getLocal() メソッドの中ではローカル・クラス Local が宣言されています。Local クラスは Test クラスのインスタンスと関連しているため、Test クラスのインスタンス変数 str に単純名でアクセスすることができます。Local ローカル・クラスは、getLocal() メソッドのブロック以外から参照することはできません。

再利用できないクラスを記述することに意味があるのだろうかという疑問があるかもしれません。しかし、囲んでいるクラスに密接に関係しなければ実現できない内部クラスが必要な状況ではあるが、セキュリティの問題上クラスを公開することは望ましくない場合などはローカル領域で宣言する方が効果的です。ローカル・クラスには外部からインスタンスを制御するためにインタフェースを実装させ、メソッド内で生成したローカル・クラスをインタフェース型として返します。これによって、実体を完全隠蔽しながらも、外部から必要な機能にアクセスすることができる特殊な構造を実現できます。

コード2
interface IShow { void show(); }

class Test {
	String name = "Kitty on your lap";
	public static void main(String args[]) {
		Test.createShow().show();
		new Test().getShow().show();
	}

	public static IShow createShow() {
		class Local implements IShow {
			public void show() {
				System.out.println(toString() + " : Test . createShow() . Local");
			}
		}
		return new Local();
	}
	public IShow getShow() {
		class Local implements IShow {
			public void show() {
				System.out.println(toString() + " : " + name);
			}
		}
		return new Local();
	}
}
実行結果
>java Test
Test$1$Local@5224ee : Test . createShow() . Local
Test$2$Local@5ff48b : Kitty on your lap

コード2は、システムを隠蔽しつつ、プログラマに統一した情報提供手段を実現するために使う常套手段の一つです。インタフェースを通して情報にアクセスさせながら、インタフェースを実装するクラスは利用者に見えないような仕組になっています。クラス利用者に対する隠蔽はパッケージ内でクラスを非公開にすればそれでよいのですが、実装をローカル・クラス化することによって強力な隠蔽工作が実現できます。例えば、同じ開発チームに対する隠蔽などにも有効な手段となるでしょう。

メンバ・クラスやローカル・クラスなどを実践で有効活用するには、設計論に関する知識が重要になります。これは、理屈だけではなく経験と発想も重要になる分野なので、今は内部クラスの意義を十分に理解できなくても大きな問題ではありません。仕様を理解していれば良いでしょう。