本日のカクテルは Enclosing Inner Classです。
日本語ではエンクロージング内部クラスなどといわれます。
Javaの内部クラスはいくつか種類があり、宣言とインスタンス生成の仕方は以下のようになります。
public class Outer {
/** staticな内部クラスの宣言 */
public static class StaticInner { }
/** エンクロージング内部クラスの宣言 */
public class EnclosingInner { }
public static void main(String[] args) {
// ローカル内部クラスの宣言
class LocalInner { }
// staticな内部クラス
StaticInner staticInner = new StaticInner();
// エンクロージング内部クラス
Outer outer = new Outer();
EnclosingInner enclosingInner = outer.new EnclosingInner();
// ローカル内部クラス
LocalInner localInner = new LocalInner();
// 無名クラス
Runnable runnable = new Runnable() {
public void run() {
}
};
}
}
今回はこのうちエンクロージング型に絞って話をしましょう。
Outer.javaファイルの中にはOuterクラスが宣言されます。javaファイルにはファイル名と同じクラスが宣言される。これは基本ですね。
内部クラスは、これらトップレベルクラスの内側に宣言されるのです。
エンクロージング型の内部クラスの特徴はOuterクラスのインスタンスと結びつきがあるということです。
newするときには、newの前に外側のクラスのインスタンスを書きます。
あたかもOuterクラスのメソッドのような扱いですね。newの主語はOuterのインスタンスなのです。
ちなみに、Outerクラスのインスタンスメソッド内でエンクロージング内部クラスをnewする場合は
public void hoge() {
EnclosingInner enclosingInner = this.new EnclosingInner();
}
というようにthis.new となります。
そしてthisは省略可能ですから
EnclosingInner enclosingInner = new EnclosingInner();
というようになります。ですから、省略しない書き方を知らない人が結構いますね。
このエンクロージング内部クラスの内側からはnewするときに指定したOuterクラスのインスタンスが参照できます。
単一のOuterクラスを元に、複数エンクロージング内部クラスのインスタンスを生成すると、まるでstaticフィールドのごとく、
同一のOuterクラスのフィールドが見えるのです。
public class Outer {
public int outerParam;
/** エンクロージング内部クラスの宣言 */
public class EnclosingInner {
private int innerParam;
public void hoge() {
// Outerクラスのフィールドを参照
System.out.println(Outer.this.outerParam);
// Innerクラスのフィールドを参照
System.out.println(this.innerParam);
}
}
}
この1対多のオブジェクトの関係は、まるでClassとインスタンスの関係のようですね。
シャノンさんのオブジェクト≠インスタンスという記事では
Javaのクラスそのものがインスタンスなのだ、という話がされています。
staticというのはエンクロージング内部クラスにとってのOuterクラスのような存在なのですね。
トップレベルのクラスのnewの主語はこのjava.lang.Classのインスタンスになるのですね。
そして、java.lang.Classのインスタンスというのは通常は単一なのですが、ClassLoaderが変わると別物として振舞うのです。
違うClassLoaderでロードされたClassというのは、同じ型の別インスタンスのような関係になります。
Javaのオブジェクト指向はこのような階層構造を持っている、ちょっと拡張された概念のようです。
投稿日時 : 2007年7月31日 23:05