Deep Side of Java〜Java 言語再入門 第1回

オブジェクト指向言語の実装

オブジェクト指向言語の実装

「多相」のような動的機構があるとはいっても、オブジェクト指向は基本的にコンパイラ言語であり、このような機構を特定の実行環境で実現しているわけではない。この動的機構のミソは、隠しフィールドにある。インスタンスはある構造体へのポインタに過ぎないのである。つまり、すべてのインスタンスは、定義された変数のフィールドを持つのは勿論だが、その他に1つだけ隠しフィールドを持ち、これがクラス情報に対するポインタになっている。

クラス情報で重要なのは当然そのクラスに属するメソッドのテーブル、関数ポインタのテーブルである。だから、メソッド呼び出しは次のようなコードに変換される。それゆえ、どんな形にキャストされたとしても、作成された時の型を決して見失わないのである。これはコンパイル時に決定可能であることに注意されたい。

instance.method( arg );
→
(*instance->hidden->table[5])( arg );

では、継承による動的多相はどうだろう。これは「継承」をする際に、派生クラスはすべての基底クラス(親から祖父から...最後はルートに当る Object クラスまで)のメソッドをすべて引き継ぐ。つまり、「継承」の定義として、直接の親クラスのメソッド・テーブルをコピーし、それに新たに定義されたメソッドを追加し、上書きをするメソッドでは、そのメソッド番号の関数ポインタを、新たな定義を示す関数ポインタに書き換える。これによって継承と多相が可能になるのである。

Java では実際には、マルチスレッド環境で動的ガベージコレクション(いらなくなったインスタンスを自動的に判別し、メモリ利用効率を上げるためにそれを回収して再利用する賢い機能)が実装されているために、インスタンスは一種のダブルポインタであり、特に「ハンドル」と呼ばれる。ここらへんについて詳しく知りたい人は「Super Technique 講座〜ガベージコレクション」を参照してくれたまえ。

「Java 仮想マシン仕様」によると、「クラス・インスタンスへの参照は、それ自身が1組のポインタを形成しているハンドルへのポインタとなっている。1つは、オブジェクトのメソッドと、オブジェクトの型を表す Class オブジェクトへのポインタを保持しているテーブルへのポインタであり、もう1つは、オブジェクト・データに対して Java のヒープから割り当てられるメモリへのポインタである。」と記述されている(3.7)。これは C++ のような純粋なコンパイラ言語の仕様とは異なり、次のような定義であることになる。

struct Instance {
    void *memory;
    Class *info;
};

....................
    /* SomeClass sc = new SomeClass();  は模式的に次のようになる */
    struct Instance *sc = (struct Instance *)malloc( sizeof(struct Instance) );
    sc->memory = malloc( sizeof(struct SomeClass_data) );
    sc->info = (Class)malloc( sizeof(ClassInfo) );
    /* .class ファイルのクラス情報から、そのクラスのクラス情報構造体を作り上げる */
    setClassInfo( sc->info, "SomeClass" ); 
    /* コンストラクタを呼び出す */
    sc->info->constructor( sc->memory );

それゆえ、Java の動作はかなりインタプリタ風である。たとえば、そのインスタンスからそれが属する「クラスの名前」が取得できたり(getClass メソッド)、Class クラスから、それが持つ変数フィールドやメソッドをすべて拾い出す機能(reflection)が実現されていたりするのである。



copyright by K.Sugiura, 1996-2006