目次 | 前項目 | 次項目 内部クラス仕様


Java 1.1 クラスに対する新しいバイナリ互換要件とはなにか?

Java 1.1 コンパイラに出力された異なるベンダからのバイトコード間でバイナリ互換を保証し、デバッガおよび同様のツールのこれらバイトコードに対する適切な適用可能性を保証するために、Java は生成されるバイトコードの形式にある要件を課します。この節では、各種の内部クラスとネストしているトップレベルクラスの実装に適した、Java 1.1 の新しい要件について説明します。

クラスおよびインタフェースのバイトコード名

Java Virtual Machines のインスタンスおよび Java バイトコードは、Java ソースコー ドで使用される名前と詳細には異なるバイトコード名によって参照型を参照します。パッケージメンバ T のバイトコード名は、すべての `.' を `/' で置き換え、(パッケージ名が null でなければ)別の `/' と簡単な名前 T を後に付けて、パッケージ名として定義します。T のバイトコード名はまた、T の本体内で定義されるすべてのクラスのバイトコード名の前置として働きます。

別のクラスの非 private メンバであり、どのブロックまたは private クラスにも(直接または間接に)含まれないクラス C のバイトコード名は、即座に親クラスのバイトコード名として定義されます。このバイトコード名は `$' と簡単な名前 C が後に付きます。

他のすべてのクラスは、アクセス不可能と呼ばれます。アクセス不可能クラス N は、決して他のコンパイル単位のコードによって参照できません。こうして、コンパイラが同じコンパイル単位の他のクラスと衝突しないように N の名前を選択する限り、その名前はグローバルに一意です。それは、その前置(以前に必要になったように)が、それが発生するパッケージメンバに対して一意だからです。

ツールのために、アクセス不可能クラス N の名前付けについて、いくつかの補足用件があります。このバイトコード名は親クラス(メンバの場合は即座に親クラス)のバイトコード名に、`$' とコンパイラが選択した正の 10 進数字、または `$' と簡単な名前 N 、または両方が(この順番で)後に付きます。さらに、ブロック local N のバイトコード名は、結果の名前を一意にする場合には、親パッケージメンバ T、文字 `$1$' および N から構成します。

ClassgetName メソッドが生成する文字列は、これらの ケースのすべてにおいて、`/' を `.' で置き換えてバイトコード名から引き出されます。これを Java ソースコードに似せるために、名前を「掃除」しようとはしません。

クラス属性 InnerClasses

Java 1.1 コンパイラのバイトコード出力は、(CONSTANT_Class 入力を経由して)パッケージメンバでないクラスまたはインタフェースのバイトコード名を参照する可能性があります。その場合、バイトコードにはまた、これらの名前のエンコーディングを宣言する InnerClasses と呼ばれるクラス属性が入っている必要もあります。この属性は各エンコード名に対して 1 つ、次のレコードの配列を含みます:

    InnerClasses_attribute {
      u2 attribute_name_index;
      u4 attribute_length;
      u2 number_of_classes;
      {
        u2 inner_class_info_index;   // CONSTANT_Class_info index
        u2 outer_class_info_index;   // CONSTANT_Class_info index
        u2 inner_name_index;         // CONSTANT_Utf8_info index
        u2 inner_class_access_flags; // access_flags bitmask      } classes[number_of_classes]
    }

各配列要素はエンコード名、定義スコープ、簡単な名前、および当初宣言され、変形されていないアクセスフラグのビットマスクを付けてクラスを記録します。内部クラスがメンバでない場合、その outer_class_info_index はゼロです。クラスが匿名の場合、その inner_name_index はゼロです。

クラス Cprotected と宣言された場合、public アクセスフラグビットはCaccess_flags フィールドにセットされたとしても、InnerClasses レコード内で清掃されます。

レコードの outer_class_info_index は自分自身パッケージメンバでないクラス E を参照し、同じ InnerClasses 属性の前のレコードは E を記述する必要があります。

クラスが型であるメンバを持つ場合、この型の各々にレコードを付けた InnerClasses 属性を持つ必要があります。既に与えられた規則は、パッケージメンバでないクラスが、それに対してレコードを持つ InnerClasses 属性、および最も外側のものを除く親クラスのすべてを持つことを暗示します。

これらの規則は、コンパイラとデバッガが分解せずに、また内部クラス定義を調べるために追加のファイルを開くことなしに、正しくバイトコードを解釈できることを保証します。コンパイラはアクセス不可能クラスに対して InnerClasses レコードを省略できますが、すべてのクラスにレコードを含めるように推奨されます。特にコードをデバッガで使用するためにコンパイルするときがそうです。

メンバ属性 Synthetic

以前に検討したように、コンパイラは名前のスコーピングを実装するために、特定の覆い隠されたフィールドとメソッドを合成します。これらのフィールドは、特に記されていない場合、private であり、さもなければパッケージスコープ程度です。

バイトコードを生成するとき、Java 1.1 コンパイラはソースコードで直接定義されてい ないフィールドまたはメンバすべてを、 Synthetic という名前の属性でマークするように要求されます。(現在、長さはゼロでなければなりません。) これによって、他のコンパイラが private でない覆い隠されたメンバに不注意なソースレベルの参照をしないようにし、ツールが不必要にこれらを表示しないようにできます。

(局所変数を Synthetic であると宣言するための対応する機構を、また導入する可能性があります。)

Java 1.1 コンパイラは、内部クラスを実装するとき次の名前付け規則を使用するように、要求はされないとしても強く薦められます。コンパイラは、他の目的で個々で定義した合成名を使用してはいけません。

最も外側の親インスタンスにポイントする合成フィールドは this$0 と名付けられます。次に外側の親インスタンスは this$1 などとなります。(このようなフィールドがせいぜい 1 つ、すべての与えられた内部クラスに必要です。) 定数 v のコピーを含む合成フィールドは val$v と名付けられます。これらのフィールドは final です。

これら合成フィールドのすべては、初期化するフィールドと同じ名前を持つ構築子パラメータによって初期化されます。パラメータの 1 つが最奥の親インスタンスの場合、これが最初になります。このような構築子パラメータのすべては合成になります。コンパイラが合成フィールド値を構築子のコードの中だけで使用すると決めた場合、フィールド自身を省略し、パラメータだけを使用して変数参照を実装します。

private メンバまたは構築子へのアクセスを供与する private でない final 合成メソッドは、access$N という形式の名前を持ちます。ここで、N は 10 進数字です。このようなアクセスプロトコルの組織は指定されていません。

1.1 互換のデバッガおよび同様のツールは、これらの名前付け規則を認識し、変数表示とシンボルテーブルをこれにしたがって編成する必要があります。ツールがこれらの名前を分解する必要があることに注意してください。コンパイラは少なくともデフォルトで、これらの規則を使用するよう強く薦められます。

Java Virtual Machine の実装は変化し、ここで指定される合成メンバが正しく定義され、使用されるように要求する可能性があります。最適化技術を合成メンバに適用して、その性質を開拓することは理にかないます。


目次 | 前項目 | 次項目

内部クラス仕様 (HTML generated by dkramer on March 15, 1997)
Copyright (c) 1996, 1997 Sun Microsystems, Inc. All rights reserved
コメントや訂正は john.rose@eng.sun.com 宛てに送ってください。