クラスおよびインタフェースに関する一貫した用語

Java®言語仕様バージョン16+37-2232への変更

このドキュメントでは、クラスおよびインタフェースに関連する用語とタクソノミの使用方法を明確にして、クラスおよびインタフェースとをよりはっきりと区別するためのJava言語仕様の変更について説明します。

代表的な用語は次のとおりです。クラス宣言またはインタフェース宣言は、それぞれクラスまたはインタフェースを導入する構文構造です。個々のクラスおよびインタフェース宣言は構文形式が異なり、文法によって、異なるコンテキストに出現することがあります。enum宣言は、特別な種類のクラスであるenumクラスを導入します。注釈宣言は、特別な種類のインタフェースである注釈インタフェースを導入します。

クラス型またはインタフェース型は変数または式の型です。この場合、型によってクラスまたはインタフェースに名前が付けられます。クラスまたはインタフェース自体、あるいはその宣言について説明しているときは、という語を避ける必要があります。たとえば、「メンバー型」は不適切です。(例外として、クラス、インタフェースまたは型パラメータを指す名前の説明に型名を引き続き使用します。)

各クラスは直接スーパークラス型直接スーパーインタフェース型を持ちます。これらは、型によって指定される追加の情報(型引数など)が無関係の場合に、直接スーパークラス直接スーパーインタフェースにマップできます。推移閉包により、スーパークラス型スーパーインタフェース型スーパークラスおよびスーパーインタフェースについての一般的な詳細を説明できます。

変更は、Java言語仕様の既存のセクションについて説明しています。新しいテキストはこのように示され、削除されたテキストはこのように示されます。必要に応じて、説明と考察が端の方にグレーのボックスで囲まれて記載されています。

第1章: 概要

1.1 仕様の編成

第2章では、言語の字句および構文文法を表すために使用する文法および表記法について説明します。

第3章では、CおよびC++に基づくJavaプログラミング言語の字句構造について説明します。この言語は、Unicode文字セットで書かれています。これは、ASCIIのみをサポートするシステム上のUnicode文字の書込みをサポートしています。

第4章では、型、値および変数について説明します。型は、プリミティブ型および参照型に細分されます。

プリミティブ型は、すべてのマシン上およびすべての実装内で同じであるよう定義されており、2の補数整数、単精度および倍精度のIEEE 754標準浮動小数点数、boolean型、およびUnicode文字のchar型の様々なサイズがあります。プリミティブ型の値は、状態を共有しません。

参照型には、クラス型、インタフェース型および配列型があります。参照型は、クラスまたは配列のインスタンスである、動的に作成されたオブジェクトによって実装されます。各オブジェクトに対する複数の参照が存在する場合があります。すべてのオブジェクト(配列を含む)が、クラス階層の(単一)ルートであるクラスObjectのメソッドをサポートしています。事前定義されたStringクラスは、Unicode文字列をサポートしています。オブジェクト内のプリミティブ値をラップするためのクラスが存在します。多くの場合、ラップおよびラップ解除は、コンパイラによって自動的に実行されます(この場合、ラップはボックス化と呼ばれ、ラップ解除はボックス化解除と呼ばれます)。クラスおよびインタフェース宣言クラスおよびインタフェースは汎用です。つまり、これらは他の参照型によってパラメータ化される場合があります。そのような宣言そのようなクラスおよびインタフェースのパラメータ化された型は、その後特定の型引数によって呼び出されるを提供する場合があります。

変数は、型付きの記憶域の場所です。プリミティブ型の変数には、そのプリミティブ型の値が格納されます。クラス型の変数には、null参照、またはそのクラス型である型を持つ名前付きクラスのインスタンスであるオブジェクトへの参照、あるいはそのクラスのサブクラスへの参照を格納できます。インタフェース型の変数には、null参照、または名前付きインタフェースを実装するクラスのインスタンスへの参照を格納できます。配列型の変数には、null参照、または配列への参照を格納できます。Objectクラス型の変数には、null参照、または(クラス・インスタンスと配列のどちらであるかとは関係なく)任意のオブジェクトへの参照を格納できます。

第5章では、変換および数値昇格について説明します。変換により、コンパイル時の型、および場合によっては式の値が変更されます。これらの変換には、プリミティブ型と参照型の間のボックス化およびボックス化解除変換が含まれます。数値昇格を使用して、数値演算子のオペランドを、演算が実行可能な共通型に変換します。この言語にはループホールがありません。参照型でのキャストは、型の安全性を確保するために実行時にチェックされます。

第6章では、宣言および名前について説明するとともに、名前が意味するもの(つまり、名前がどの宣言を表しているか)を確認する方法について説明します。Javaプログラミング言語では、クラス、インタフェースまたはメンバーを使用する前にこれらを宣言する必要はありません。宣言順序が重要なのは、ローカル変数、ローカル・クラスの場合、およびクラスまたはインタフェース内のフィールド・イニシャライザの順序の場合のみです。ここでは、読みやすいプログラムの作成に役立つ、推奨される命名規則について説明します。

第7章では、パッケージに組み込まれるプログラムの構造について説明します。パッケージのメンバーは、クラス、インタフェースおよびサブパッケージです。パッケージ(およびその結果としてメンバー)は、階層ネームスペース内に名前があります。通常、パッケージ名はインターネット・ドメイン名システムを使用して一意に構成できます。コンパイル・ユニットは、特定のパッケージのメンバーであるクラスおよびインタフェースの宣言を含み、他のパッケージからクラスおよびインタフェースをインポートしてそれらに短い名前を付けることができます。

パッケージは、非常に大規模なプログラムの構築時にビルディング・ブロックとして機能するモジュールにグループ化できます。モジュールの宣言により、独自のパッケージ内でコードをコンパイルおよび実行するために必要な他のモジュール(およびその結果としてのパッケージと、その結果としてのクラスおよびインタフェース)を指定します。

Javaプログラミング言語は、パッケージのメンバー、クラスおよびインタフェースに対する外部アクセスに関する制限をサポートしています。パッケージのメンバーは、同じパッケージ内の他のメンバー、同じモジュール内の他のパッケージのメンバー、または異なるモジュール内のパッケージのメンバーからのみアクセスできます。クラスおよびインタフェースのメンバーにも同様の制約が適用されます。

第8章では、クラスについて説明します。クラスのメンバーは、クラス、インタフェース、フィールド(変数)およびメソッドです。クラス変数はクラスごとに1つ存在します。クラス・メソッドは、特定のオブジェクトを参照せずに動作します。インスタンス変数は、クラスのインスタンスであるオブジェクト内に動的に作成されます。インスタンス・メソッドは、クラスのインスタンス上で呼び出されます。このようなインスタンスは、実行時に現在のオブジェクトthisになり、オブジェクト指向のプログラミング・スタイルをサポートします。

クラスは単一継承をサポートしており、この場合、各クラスが単一のスーパークラスを持ちます。各クラスは、スーパークラスからメンバーを継承し、最終的にはクラスObjectから継承します。クラス型の変数は、その名前付きクラスのインスタンスまたはそのクラスのサブクラスのインスタンスを参照できるため、既存のメソッドで新しいクラスを多相的に使用できます。

クラスは、synchronizedメソッドを使用した同時プログラミングをサポートします。メソッドは、実行によって発生する可能性があるチェック済例外を宣言します。これにより、コンパイル時のチェックが可能になり、例外条件を確実に処理できるようになります。オブジェクトは、オブジェクトがガベージ・コレクタによって破棄される前に呼び出されるfinalizeメソッドを宣言します。これにより、オブジェクトが状態をクリーン・アップできるようになります。

簡潔にするために、この言語には、クラスの実装とは別個の宣言「ヘッダー」や、別個の型およびクラス階層はありません。

特別な形式のクラスであるenum enumクラスは、小規模な値セットの定義およびその操作を型安全な方法でサポートします。他の言語の列挙とは異なり、enum enum定数はオブジェクトであり、独自のメソッドを持つ場合があります。

第9章では、インタフェースについて説明します。インタフェースのメンバーは、クラス、インタフェース、定数フィールドおよびメソッドです。それ以外には無関係なクラスも、同じインタフェースを実装できます。インタフェース型の変数には、そのインタフェースを実装する任意のオブジェクトへの参照を含めることができます。

クラスおよびインタフェースは、インタフェースからの複数の継承をサポートします。1つ以上のインタフェースを実装するクラスは、スーパークラスとスーパーインタフェースの両方からインスタンス・メソッドを継承できます。

注釈インタフェースは、宣言に注釈を付けるために使用される特別なインタフェースです。このような注釈は、いかなる方法であれJavaプログラミング言語内のプログラムのセマンティクスに影響を及ぼすことが許可されません。ただし、これらは、様々なツールに対して有用な入力を提供します。

第10章では、配列について説明します。配列アクセスには、境界のチェックが含まれます。配列は、動的に作成されたオブジェクトであり、Object型の変数に割り当てることができます。この言語は、多次元の配列ではなく、配列の配列をサポートします。

第11章では、例外について説明します。例外は再開されるものではなく、言語のセマンティクスおよび同時メカニズムと完全に統合されます。例外には、チェック例外、ランタイム例外およびエラーの3種類があります。コンパイラでは、メソッドまたはコンストラクタの結果としてチェック例外が発生するのはメソッドまたはコンストラクタがチェック例外を宣言する場合のみとするよう求めることにより、チェック例外が適切に処理されるよう徹底します。これにより、例外ハンドラが存在するかどうかをコンパイル時にチェックできるようにし、大規模なプログラミングを支援します。ほとんどのユーザー定義の例外はチェック例外にする必要があります。Java仮想マシンによってプログラム内で無効な例外が検出されると、NullPointerExceptionなどのランタイム例外が発生します。Java仮想マシンによって失敗が検出されると、OutOfMemoryErrorなどのエラーが発生します。ほとんどの単純なプログラムでは、エラー処理が試行されません。

第12章では、プログラムの実行時に発生するアクティビティについて説明します。プログラムは通常、コンパイルされたクラスおよびインタフェースを表すバイナリ・ファイルとして格納されます。これらのバイナリ・ファイルをJava仮想マシンにロードし、他のクラスおよびインタフェースとリンクし、初期化できます。

初期化後は、クラス・メソッドおよびクラス変数を使用できます。一部のクラスはインスタンス化し、クラス型の新しいオブジェクトを作成できます。クラス・インスタンスであるオブジェクトには、クラスの各スーパークラスのインスタンスも含まれます。オブジェクトの作成には、これらのスーパークラス・インスタンスの再帰的な作成が含まれます。

オブジェクトが作成されなくなると、ガベージ・コレクタによって回収される場合があります。オブジェクトがファイナライザを宣言すると、このファイナライザがオブジェクトの再利用前に実行され、ファイナライザがなければリリースされなかったはずのリソースをクリーン・アップする機会がオブジェクトに与えられます。クラスが必要なくなると、クラスはアンロードされる場合があります。

第13章では、バイナリの互換性について説明し、クラスおよびインタフェースを変更すると、変更されたクラスおよびインタフェースを使用しているが再コンパイルされていない他のクラスおよびインタフェースに与える影響を示します。これらの考慮事項は、一般的にはインターネットを介して連続的なバージョンのシリーズとして広範に配布されるクラスおよびインタフェースの開発者に関連するものです。優れたプログラム開発環境では、クラスまたはインタフェースが変更されるたびに依存コードが自動的に再コンパイルされるため、ほとんどのプログラマはこれらの詳細を意識する必要はありません。

第14章では、CおよびC++に基づくブロックおよび文について説明します。この言語にはgoto文はありませんが、ラベル付きのbreakおよびcontinue文が含まれます。Cとは異なり、Javaプログラミング言語では、制御フロー文にboolean (またはBoolean)式が必要であり、コンパイル時により多くのエラーを捕捉することを期待して、(ボックス化解除を介す場合を除き)型を暗黙的にbooleanには変換しません。synchronized文は、基本的なオブジェクトレベルのモニター・ロックを提供します。tryには、ローカルでない制御の転送を防ぐためにcatchおよびfinally句を含めることができます。

第15章では、式について説明します。このドキュメントでは、決定論と移植性を高めるために、式の評価の(明白な)順序を完全に規定します。オーバーロードのメソッドおよびコンストラクタは、適用可能なものの中から最も的確なメソッドまたはコンストラクタを選択することにより、コンパイル時に解決されます。

第16章では、この言語でローカル変数が使用される前に確実に定義されるようにする明確な方法について説明します。他のすべての変数は自動的にデフォルト値に初期化されますが、Javaプログラミング言語では、プログラミング・エラーのマスキングを回避するためにローカル変数は自動的には初期化されません。

第17章では、スレッドおよびロックのセマンティクスについて説明します。これらは、当初はMesaプログラミング言語に導入されたモニターベースの同時実行性に基づいています。Javaプログラミング言語では、高パフォーマンスの実装をサポートする共有メモリーのマルチプロセッサのメモリー・モデルを規定します。

第18章では、汎用メソッドの適用性をテストし、汎用メソッドの呼出し時に型を推測するために使用される様々な型インタフェース・アルゴリズムについて説明します。

第19章では、この言語の構文文法を示します。

第2章: 文法

2.4 文法の表記法

...

右側が非常に長い場合は、2行目を明確にインデントすることによって2行目に続けることができます。

たとえば、構文文法には次のプロダクションが含まれます。

NormalClassDeclaration:
{ClassModifier} class TypeIdentifier [TypeParameters]
[Superclass ClassExtends] [Superinterfaces ClassImplements] ClassBody

非ターミナルのNormalClassDeclarationの1つの右側が定義されます。

8.1の変更を反映します。

...

第4章: 型、値および変数

4.5 パラメータ化された型

汎用のクラスまたはインタフェース宣言(8.1.29.1.2)では、パラメータ化された型のセットを定義します。

パラメータ化された型は、形式C<T1,...,Tn>のクラスまたはインタフェース型です。この場合、Cは汎用クラスまたはインタフェースの名前で、<T1,...,Tn>は汎用クラスまたはインタフェースの特定のパラメータ化を示す型引数のリストです。

汎用クラスまたはインタフェースは、型パラメータF1,...,Fnと、対応する境界B1,...,Bnを持ちます。パラメータ化された型のそれぞれの型引数Tiの対象は、対応する境界にリストされたすべての型のサブタイプであるすべての型です。つまり、Biのそれぞれの境界型Sについては、TiS[F1:=T1,...,Fn:=Tn]のサブタイプになります(4.10)。

次がすべて当てはまる場合、パラメータ化された型C<T1,...,Tn>整形式です。

パラメータ化された型が整形式ではない場合は、コンパイル時にエラーが発生します。

この仕様では、クラスまたはインタフェース型に言及する場合は、常に、明示的に除外されていないかぎり、汎用バージョンパラメータ化された型も含まれます。

次のいずれかが当てはまる場合、2つのパラメータ化された型は明確に異なります

8.1.2の例の汎用クラスを考慮した場合の、整形式のパラメータ化された型を次にいくつか示します。

これらの汎用クラスの間違ったパラメータ化を次にいくつか示します。

パラメータ化された型は、ネストされた汎用クラスまたはインタフェースのパラメータ化になる場合があります。たとえば、非汎用クラスCに汎用メンバー・クラスDと型パラメータ<T>がある場合、C.D<Object>がパラメータ化された型になります。また、汎用クラスCと型パラメータ<T>に非汎用メンバー・クラスDがある場合、クラスDが汎用でない場合でも、メンバー・クラスC<String>.Dがパラメータ化された型になります。

4.5.2 パラメータ化された型のメンバーおよびコンストラクタ

Cは、型パラメータA1,...,Anを持つ汎用クラスまたはインタフェース宣言C<T1,...,Tn>は、Cのパラメータ化であるとします。ここで、1 i nとすると、Tiは(ワイルドカードではなく)型です。次に、

Cのパラメータ化の型引数のいずれかがワイルドカードである場合、次のようになります。

パラメータ化された型のメンバーにアクセスするにはキャプチャ変換を実行する必要があり、かつ、クラス・インスタンス作成式(15.9)のキーワードnewの後にはワイルドカードを使用できないため、このことはまったく重大ではありません。

前の段落の唯一の例外は、ネストされたパラメータ化された型がinstanceof演算子(15.20.2)の式として使用される場合となり、この場合、キャプチャ変換は適用されません。

汎用型宣言クラスまたはインタフェースで宣言されるstaticメンバーは、汎用型に対応する非汎用型汎用クラスまたはインタフェースの名前を使用して参照される必要があります(6.16.5.5.26.5.6.2)。そうしないと、コンパイル時にエラーが発生します。

つまり、パラメータ化された型を使用して、汎用型宣言で宣言されたstaticメンバーを参照することは不正になります。

4.8 RAW型

非汎用レガシー・コードとの連携を促進するために、パラメータ化された型(4.5)のイレイジャ(4.6)、または要素型がパラメータ化された型である配列型(10.1)のイレイジャを型として使用できます。そのような型はRAW型と呼ばれます。

より正確に言うと、RAW型は次のいずれかとして定義できます。

非汎用クラスまたはインタフェースの型はRAW型ではありません。

RAW型のstaticメンバー型内部メンバー・クラスの名前がRAWとみなされる理由を確認するために、次の例を考えてみます。

class Outer<T>{
    T t;
    class Inner {
        T setOuterT(T t1) { t = t1; return t; }
    }
}

Innerのメンバーの型は、Outerの型パラメータに依存します。Tの有効なバインディングが存在しないため、OuterがRAWの場合は、InnerもRAWとして扱われる必要があります。

このルールは、継承されない型メンバー内部メンバー・クラスにのみ適用されます。型変数に依存する継承される型メンバー内部メンバー・クラスは、このセクションで後述する、RAW型のスーパータイプが消去されるルールの結果として、RAW型として継承されます。

前述のルールには、RAW型の汎用内部クラスは、それ自体のみをRAW型として使用できるという意味もあります。

class Outer<T>{
    class Inner<S> {
        S s;
    }
}

Innerには、部分的なRAW型(「未加工の」型)としてはアクセスできません。

Outer.Inner<Double> x = null;  // illegal
Double d = x.s;

Outer自体がRAWであるため、Innerを含むそのすべての内部クラスはRAWになり、型引数をInnerに渡すことができません。

RAW型のスーパークラススーパークラス型(それぞれ、スーパーインタフェーススーパーインタフェース型)は、汎用型のパラメータ化名前付きクラスまたはインタフェーススーパークラススーパークラス型(スーパーインタフェーススーパーインタフェース型)のイレイジャです。

そのスーパークラスまたはスーパーインタフェースから継承されないRAW型Cのコンストラクタ(8.8)、インスタンス・メソッド(8.49.4)または非staticフィールド(8.3)の型は、汎用クラスまたはインタフェースCに対応する汎用宣言におけるその型のイレイジャです。

メンバーがクラスまたはインタフェースDで宣言された、RAW型Cの継承されるインスタンス・メソッドまたは非staticフィールドの型は、名前がDCのスーパータイプのメンバーの型です。

クラスの継承は8.4.8で定義されていますが、RAW型を介してアクセスする場合の、継承されたメンバーの型について説明する必要があります。RAW型のスーパータイプが消去されると表現するのみでは十分ではありません。

RAW型Cstaticメソッドまたはstaticフィールドの型は、汎用クラスまたはインタフェースCに対応する汎用宣言におけるその型と同じになります。

そのスーパークラスまたはスーパーインタフェースから継承されないRAW型の非staticメンバー・クラスまたはインタフェースに型引数を渡すと、コンパイル時にエラーが発生します。

パラメータ化された型のメンバー・クラスまたはインタフェースをRAW型として使用しようとすると、コンパイル時にエラーが発生します。

これは、「未加工の」型についての禁止事項が、修飾型がパラメータ化されるが内部クラスをRAW型として使用しようとする場合にまで及ぶことを意味します。

Outer<Integer>.Inner x = null; // illegal

これは、前述のものとは反対のケースです。この不完全な型が妥当であることを示す実質的な根拠はありません。レガシー・コードでは、型引数は使用されません。非レガシー・コードでは、汎用型を適切に使用して、すべての必要な型引数を渡す必要があります。

クラスのスーパータイプはRAW型になる場合があります。このクラスのメンバー・アクセスは通常どおり扱われ、このスーパータイプのメンバー・アクセスはRAW型の場合と同様に扱われます。クラスのコンストラクタでは、superへの呼出しは、RAW型でのメソッド呼出しとして扱われます。

このことは、スーパークラスおよび他の場所での継承の処理に関連して説明されています(8.1.48.38.4.8などを参照)。

RAW型の使用は、レガシー・コードの互換性の便宜的措置としてのみ許可されています。Javaプログラミング言語に汎用が導入された後に作成するコードでRAW型を使用することはお薦めしません。今後のバージョンのJavaプログラミング言語でRAW型を使用できなくなる可能性があります。

型指定ルールの潜在的な違反が常に確実に通知されるように、RAW型のメンバーがアクセスされると、特定の場合に、コンパイル時に未チェック警告が発生します。RAW型のメンバーまたはコンストラクタにアクセスしたときのコンパイル時の未チェック警告に関するルールは、次のとおりです。

...

4.9 交差型

交差型はT1 & ... & Tn (n > 0)という形式になり、Ti (1 i n)が型です。

交差型は、型パラメータの境界(4.4)およびキャスト式(15.16)から導出できます。これらは、キャプチャ変換(5.1.10)および上限の最小値の計算(4.10.4)のプロセスでも発生します。

交差型の値は、1 i nのすべての型Tiの値となるオブジェクトです。

すべての交差型T1 & ... & Tnは、次のように、交差型のメンバーを識別する目的で概念的なクラスまたはインタフェースを帰納します。

交差型のメンバーは、それによって帰納されるクラスまたはインタフェースのメンバーになります。

交差型と型変数の境界の違いは、深く検討する価値があります。すべての型変数の境界は、交差型を帰納します。この交差型は、一般的に、単一の型で構成される些細なものです。境界の形式は、特定の不自然な状況が発生しなくなるように制限(クラスまたは型変数にできるのは最初の要素のみ、かつ境界に出現できる型変数は1つのみ)されます。ただし、キャプチャ変換によって、配列型などのより一般的な境界を持つ型変数が作成される可能性があります。

4.10 サブタイプ

4.10.2 クラスおよびインタフェース型間のサブタイプ

非汎用型宣言クラスまたはインタフェースCの場合、C型の直接スーパータイプは、次のすべてです。

<F1,...,Fn> (n > 0)の型パラメータを持つ汎用型宣言クラスまたはインタフェースCの場合、RAW型C (4.8)の直接スーパータイプは、次のすべてです。

汎用型宣言C<F1,...,Fn> (n > 0)の場合、汎用型C<F1,...,Fn>直接スーパータイプは、次のすべてです。

汎用型は型の個別の種類ではありません。8.1.4および8.1.5で説明されているとおり、汎用クラスはスーパータイプを持ちます。後述のとおり、パラメータ化された型はスーパータイプを持ちます。

<F1,...,Fn> (n > 0)の型パラメータを持つ汎用型宣言クラスまたはインタフェースCの場合、パラメータ化された型C<T1,...,Tn> (それぞれのTi (1 i n)は型)の直接スーパータイプは、次のすべてです。

<F1,...,Fn> (n > 0)の型パラメータを持つ汎用型宣言クラスまたはインタフェースCについて、パラメータ化された型C<R1,...,Rn> (Ri (1 i n)の少なくとも1つがワイルドカード型引数)の直接スーパータイプは、C<R1,...,Rn>にキャプチャ変換を適用した結果(5.1.10)であるパラメータ化された型C<X1,...,Xn>の直接スーパータイプです。

交差型T1 & ... & Tnの直接スーパータイプはTi (1 i n)です。

型変数の直接スーパータイプは、その境界にリストされる型です。

型変数は、その下限の直接スーパータイプです。

null型の直接スーパータイプは、null型自体以外のすべての参照型です。

4.11 型の使用場所

型は、ほとんどの種類の宣言内および特定の種類の式内で使用されます。特に、型を使用する16の型コンテキストがあります。

また、型は次としても使用されます。

最後に、Javaプログラミング言語には、型の使用方法を示す3つ2つの特別な語句があります。

型コンテキストの型の意味は、次によって与えられます。

一部の型コンテキストは、参照型をパラメータ化する方法を制限します。

型が使用される任意の型コンテキストで、プリミティブ型を表すキーワード、または参照型の単純名を表すIdentifierに注釈を付けることができます。また、配列型内のネストの目的のレベルで[の左側に注釈を付けることにより、配列型に注釈を付けることもできます。これらの場所にある注釈は型注釈と呼ばれ、9.7.4に規定されています。いくつか例を挙げます。

宣言内に現れる5つの型コンテキストは、宣言コンテキストと同じ数の構文上の場所を占有します(9.6.4.1)。

1つのプログラム内で同じ構文の場所が型コンテキストと宣言コンテキストの両方になる場合がありますが、これは、宣言の修飾子が宣言済エンティティの型の直前にくるためです。9.7.4では、このような場所内の注釈がどのように型コンテキストまたは宣言コンテキストあるいはその両方に現れると考えられるかについて説明します。

例4.11-1.型の使用方法

import java.util.Random;
import java.util.Collection;
import java.util.ArrayList;

class MiscMath<T extends Number> {
    int divisor;
    MiscMath(int divisor) { this.divisor = divisor; }
    float ratio(long l) {
        try {
            l /= divisor;
        } catch (Exception e) {
            if (e instanceof ArithmeticException)
                l = Long.MAX_VALUE;
            else
                l = 0;
        }
        return (float)l;
    }
    double gausser() {
        Random r = new Random();
        double[] val = new double[2];
        val[0] = r.nextGaussian();
        val[1] = r.nextGaussian();
        return (val[0] + val[1]) / 2;
    }
    Collection<Number> fromArray(Number[] na) {
        Collection<Number> cn = new ArrayList<Number>();
        for (Number n : na) cn.add(n);
        return cn;
    }
    <S> void loop(S s) { this.<S>loop(s); }  
}

この例では、型は次の宣言に使用されています。

および次の種類の式内:

第6章: 名前

名前を使用して、プログラム内で宣言されるエンティティを参照します。

宣言されるエンティティ(6.1)は、パッケージ、クラス型(標準またはenum)、インタフェース型(標準または注釈型)、参照型のメンバー(クラス、インタフェース、フィールドまたはメソッド)、(クラス、インタフェース、メソッドまたはコンストラクタの)型パラメータ、(メソッド、コンストラクタまたは例外ハンドラの)パラメータ仮パラメータ、例外パラメータまたはローカル変数です。

この後に、すべてのケースの列挙に特化したセクション(6.1)があります。この章の導入文でその列挙全体を繰り返すことはしません。たとえば、様々な種類のクラス宣言について後ほど説明できます。

プログラム内の名前は、単一の識別子で構成された単純名、または「.」トークンによって区切られた識別子のシーケンスで構成された修飾名のどちらかです(6.2)。

名前を取り入れるすべての宣言にはscope (6.3)があります。これは、プログラム・テキストの一部であり、その中で、宣言されたエンティティを単純名によって参照できます。

修飾名N.xを使用して、パッケージまたは参照型のメンバーを参照できます。この場合、Nは単純名または修飾名で、xは識別子です。Nがパッケージを示す場合、xはそのパッケージのメンバーです。これは、クラスまたはインタフェース型またはサブパッケージです。Nが参照型または参照型の変数を示す場合、xはその型のメンバーを示します。これは、クラス、インタフェース、フィールドまたはメソッドです。

名前の意味を確認する場合(6.5)、発生のコンテキストを使用して、同じ名前を持つパッケージ、型、変数およびメソッド間で区別します。

アクセス制御(6.6)をクラス、インタフェース、メソッドまたはフィールド宣言内で指定して、メンバーへのアクセスを許可するタイミングを制御します。アクセスはスコープとは異なる概念です。アクセスにより、プログラム・テキストの一部を指定し、その中で、宣言されたエンティティを修飾名によって参照できます。宣言されたエンティティへのアクセスは、フィールド・アクセス式(15.11)、メソッドが単純名では指定されないメソッド呼出し式(15.12)、メソッド参照式(15.13)、または修飾クラス・インスタンス作成式(15.9)にも関連します。アクセス修飾子がない場合、ほとんどの宣言はパッケージ・アクセス権を持つため、その宣言が含まれていれば、パッケージ内のどの場所でもアクセスできます。その他の選択肢は、publicprotectedおよびprivateです。

この章では、完全修飾名および正規名(6.7)についても説明します。

6.1 宣言

宣言により、エンティティがプログラムに導入され、このエンティティを参照するために名前内で使用できる識別子(3.8)が組み込まれます。この識別子には、導入されるエンティティがクラス、インタフェースまたは型パラメータである場合は型識別子であるという制約があります。

宣言されたエンティティは、次のいずれかです。

仮パラメータと例外パラメータは通常、2つの別個のエンティティとして扱います。一方、クラス・メソッドとインタフェース・メソッドの仮パラメータは1箇所のみで指定し、別個のものとして扱う必要はありません。

コンストラクタ(8.8)も宣言によって導入されますが、新しい名前が導入されるのではなく、そのコンストラクタが宣言されたクラスの名前が使用されます。

汎用ではない型の宣言(class C ...)では、1つのエンティティ(非汎用型(C))が宣言されます。非汎用型は、構文上の類似点を除いてRAW型ではありません。対照的に、汎用型(class C<T> ...またはinterface C<T> ...)の宣言では、汎用型(C<T>)および対応する非汎用型(C)の2つのエンティティが宣言されます。この場合、用語Cの意味は、この用語が出現するコンテキストによって異なります。

14種類の非汎用コンテキストは、次のとおりです。

  1. モジュール宣言内のusesまたはprovidesディレクティブ内(7.7.1)

  2. 単一型インポート宣言内(7.5.1)

  3. 単一静的インポート宣言内の.の左側(7.5.3)

  4. オンデマンド静的インポート宣言内の.の左側(7.5.4)

  5. コンストラクタ宣言内の(の左側(8.8)

  6. 注釈内の@記号の後(9.7)

  7. クラス・リテラル内の.classの左側(15.8.2)

  8. 修飾されたthis式の.thisの左側(15.8.4)

  9. 修飾されたスーパークラス・フィールド・アクセス式の.superの左側(15.11.2)

  10. 修飾されたメソッド呼出し式内の.Identifierまたは.super.Identifierの左側(15.12)

  11. メソッド参照式内の.super::の左側(15.13)

  12. 接尾辞式内の修飾された式名またはtry-with-resources文(15.14.114.20.3)

  13. メソッドまたはコンストラクタのthrows句内(8.4.68.8.59.4)

  14. 例外パラメータ宣言内(14.20)

最初の11の非汎用コンテキストは、6.5.1内のTypeNameの最初の11の構文的コンテキストに対応しています。12番目の非汎用コンテキストは、修飾されたExpressionName (C.xなど)に、静的メンバー・アクセスを示すためのTypeName Cが含まれる可能性がある場所です。これらの12のコンテキスト内でTypeNameが共通して使用されていることが重要です。これは、これらのコンテキストには、型のファーストクラス未満の使用方法が含まれることを示します。対照的に、13番目と14番目の非汎用コンテキストではClassTypeが採用されています。これは、throwsおよびcatch句には、型がファーストクラスの方法でフィールド宣言などにあわせて使用されていることを示します。これら2つのコンテキストに非汎用としての特性が与えられるのは、例外型をパラメータ化できないという事実のためです。

ClassTypeプロダクションでは注釈が許可されるため、throwsまたはcatch句内で型の使用に注釈を付けることができます。一方、TypeNameプロダクションでは注釈が許可されないため、単一型インポート宣言などの型の名前に注釈を付けることはできません。

汎用クラスまたはインタフェース(class C<T> ...またはinterface C<T> ...) (8.1.29.1.2)の宣言は、Cという名前のクラスと、RAWのCC<Foo>C<Bar>などの型のファミリの両方を導入します。

非汎用コンテキストの1つとして次に示されている汎用性が重要ではない場所でCへの参照が発生した場合、Cへの参照は、クラスまたはインタフェースCを示します。その他のコンテキストでは、Cへの参照は、Cによって導入されたまたは型の一部を示します。

14種類の非汎用コンテキストは、次のとおりです。

  1. モジュール宣言内のusesまたはprovidesディレクティブ内(7.7.1)

  2. 単一型インポート宣言内(7.5.1)

  3. 単一静的インポート宣言内の.の左側(7.5.3)

  4. オンデマンド静的インポート宣言内の.の左側(7.5.4)

  5. コンストラクタ宣言内の(の左側(8.8)

  6. 注釈内の@記号の後(9.7)

  7. クラス・リテラル内の.classの左側(15.8.2)

  8. 修飾されたthis式の.thisの左側(15.8.4)

  9. 修飾されたスーパークラス・フィールド・アクセス式の.superの左側(15.11.2)

  10. 修飾されたメソッド呼出し式内の.Identifierまたは.super.Identifierの左側(15.12)

  11. メソッド参照式内の.super::の左側(15.13)

  12. 接尾辞式内の修飾された式名またはtry-with-resources文(15.14.114.20.3)

  13. メソッドまたはコンストラクタのthrows句内(8.4.68.8.59.4)

  14. 例外パラメータ宣言内(14.20)

最初の11の非汎用コンテキストは、6.5.1内のTypeNameの最初の11の構文的コンテキストに対応しています。12番目の非汎用コンテキストは、修飾されたExpressionName (C.xなど)に、静的メンバー・アクセスを示すためのTypeName Cが含まれる可能性がある場所です。これらの12のコンテキスト内でTypeNameが共通して使用されていることが重要です。これは、これらのコンテキストには、型のファーストクラス未満の使用方法が含まれることを示します。対照的に、13番目と14番目の非汎用コンテキストではClassTypeが採用されています。これは、throwsおよびcatch句には、型がファーストクラスの方法でフィールド宣言などにあわせて使用されていることを示します。これら2つのコンテキストに非汎用としての特性が与えられるのは、例外型をパラメータ化できないという事実のためです。

ClassTypeプロダクションでは注釈が許可されるため、throwsまたはcatch句内で型の使用に注釈を付けることができます。一方、TypeNameプロダクションでは注釈が許可されないため、単一型インポート宣言などの型の名前に注釈を付けることはできません。

過去において、このドキュメントでの説明を、クラスと(場合によってはパラメータ化された)クラス型およびインタフェースと(場合によってはパラメータ化された)インタフェース型を区別して、より包括的に行うことを試みました。それがこちらの説明になります。現在では、前述の(対応するセクションの)コンテキストの前後にある用語によって、多くの部分で、型が関連しないことが明確になっています。これらは、その型ではなくクラスおよびインタフェースについて記述するコンストラクトです。

この説明は、クラスへの参照と型への参照の区別が明確になるように、導入の段落にいくつか変更を加え、注記としてここに保持されています。

命名規則

Java SEプラットフォームのクラス・ライブラリは、可能なかぎり、次の命名規則に従って選択された名前を使用しようとします。これらの命名規則は、コードをより読みやすくし、ある種の命名規則の競合を回避する上で役に立ちます。

Javaプログラミング言語で書かれたすべてのプログラムで、これらの命名規則を使用することをお薦めします。ただし、以前から使用されてきた慣例的な使用方法とは異なる場合、これらの命名規則に無条件に従う必要はありません。このため、たとえば、クラスjava.lang.Mathsinおよびcosメソッドには、ここで提案されている命名規則をこれらのメソッド名が無視する形になるとしても、これらは短く、動詞ではないため、数学的に慣例的な名前が使用されます。

...

クラスおよびインタフェースの名前

クラス型クラスの名前は、説明的な名詞または名詞句にし、過度に長くせず、大文字と小文字を混在させ、各単語の先頭を大文字にしてください。

例6.1-3.説明的なクラス名

`ClassLoader`
SecurityManager
`Thread`
Dictionary
BufferedInputStream

同様に、インタフェース型インタフェースの名前は、短く説明的にし、過度に長くせず、大文字と小文字を混在させ、各単語の先頭を大文字にしてください。この名前は、説明的な名詞または名詞句である場合があります。これは、インタフェースが抽象的なスーパークラスであるかのように使用される場合(java.io.DataInputjava.io.DataOutputなど)、またはインタフェースが動作を示す形容詞である場合(インタフェースRunnableCloneableなど)である場合に適しています。

型変数名

型変数名は、簡潔(可能であれば1文字)でありながら示唆に富む名前にする必要があります。また、小文字は含めないでください。これにより、型パラメータを一般的なクラスやインタフェースと区別しやすくなります。

コンテナ・クラスおよびインタフェースには、要素型として名前Eを使用する必要があります。マップには、キーの型としてK、および値の型としてVを使用する必要があります。任意の例外型には、名前Xを使用する必要があります。Oracleでは、型を区別するためにより具体的な名前がない場合は、型にTを使用しています。(これはたいてい、汎用メソッドの場合です。)

任意の型を示す型パラメータが複数存在する場合、アルファベットでTの隣にある文字(Sなど)を使用する必要があります。また、異なる型変数を区別するために下付き数字(T1T2など)を使用することも許容されます。このような場合、同じ接頭辞を持つすべての変数に添字を付ける必要があります。

汎用クラス内に汎用メソッドが出現する場合、混乱を避けるために、メソッドおよびクラスの型パラメータに同じ名前を使用しないようにすることをお薦めします。同じことが、ネストした汎用クラスにも当てはまります。

例6.1-4.従来の型変数名

public class HashSet<E> extends AbstractSet<E> { ... }
public class HashMap<K,V> extends AbstractMap<K,V> { ... }
public class ThreadLocal<T> { ... }
public interface Functor<T, X extends Throwable> {
    T eval() throws X;
}

型パラメータが前述のカテゴリの1つに都合よく該当しない場合、名前はできるだけ1文字の範囲内で意味のあるものとして選択する必要があります。前述の名前(EKVXT)は、指定したカテゴリに該当しない型パラメータには使用しないでください。

...

定数名

インタフェース型インタフェース内の定数の名前は、1つ以上の単語、頭字語または短縮形のシーケンスにし、すべて大文字にし、コンポーネントをアンダースコア「_」文字で区切ってください。また、クラス型クラスの変数finalの場合も、慣習に従って同様にできます。定数名は説明的なものにし、不必要に省略しないでください。これらは慣習的に、妥当な話し言葉の一部である場合があります。

定数名の例として、クラスCharacterMIN_VALUEMAX_VALUEMIN_RADIXおよびMAX_RADIXがあります。

セットの代替値、またはそれほど頻繁ではありませんが整数値のマスキング・ビットを表す定数のグループは、場合によっては名前の接頭辞として共通の頭字語を使用して指定すると役に立ちます。

次に例を示します。

interface ProcessStates {
    int PS_RUNNING   = 0;
    int PS_SUSPENDED = 1;
}

...

6.2 名前および識別子

名前は、プログラム内で宣言されるエンティティを参照するために使用されます。

名前には、単純名と修飾名の2つの形式があります。

単純名は単一の識別子です。

修飾名は、名前、「.」トークンおよび識別子で構成されます。

名前の意味を確認する場合(6.5)、名前が出現するコンテキストが考慮されます。6.5のルールは、名前がパッケージ(6.5.3)クラス、インタフェースまたは型パラメータ(6.5.5)式の変数または値(6.5.6)あるいはメソッド(6.5.7)を示す(指す)コンテキストを区別します。

パッケージおよび参照型クラス、インタフェースおよび型パラメータには、修飾名でアクセスできるメンバーがあります。修飾名の説明と名前の意味の確認の背景として、4.44.5.24.84.97.18.29.2および10.7のメンバーシップの説明を参照してください。

プログラムのすべての識別子が名前の一部となるわけではありません。識別子は、次の状況でも使用されます。

...

6.3 宣言のスコープ

宣言のスコープは、単純名を使用して、その宣言によって宣言されたエンティティを参照できるプログラム内の領域です(ただし、シャドウ化されていることが条件です) (6.4.1)。

宣言がプログラム内の特定のポイントでスコープ内にあるとされるのは、宣言のスコープにそのポイントが含まれる場合のみです。

観察可能な最上位パッケージ(7.4.3)の宣言のスコープは、パッケージが一意として表示されるモジュールに関連付けられたすべての観察可能なコンパイル・ユニットです(7.4.3)。

観察可能でないパッケージの宣言はスコープ内ではありません。

サブパッケージの宣言はスコープ内ではありません。

パッケージjavaは常にスコープ内にあります。

単一型インポート宣言(7.5.1)またはオンデマンド型インポート宣言(7.5.2)によってインポートされるクラスまたはインタフェースのスコープは、モジュール宣言(7.7)、import宣言が出現するコンパイル・ユニットのすべてのクラスおよびインタフェース宣言(7.6)と、コンパイル・ユニットのモジュール宣言またはパッケージ宣言の注釈です。

単一静的インポート宣言(7.5.3)またはオンデマンド静的インポート宣言(7.5.4)によってインポートされるメンバーのスコープは、モジュール宣言、import宣言が出現するコンパイル・ユニットのすべてのクラスおよびインタフェース宣言と、コンパイル・ユニットのモジュール宣言またはパッケージ宣言の注釈です。

最上位クラスまたはインタフェース(7.6)のスコープは、最上位クラスまたはインタフェースが宣言されるパッケージ内のすべてのクラスおよびインタフェース宣言です。

クラスまたはインタフェースCで宣言または継承されるメンバーm (8.1.6 8.29.2)の宣言のスコープは、Cの本体全体(ネストしたクラスまたはインタフェース宣言を含む)です。

インタフェース型I (9.1.4)で宣言または継承されるメンバーmの宣言のスコープは、Iの本体全体(ネストした型宣言を含む)です。

enum型Tで宣言されるenum定数Cのスコープは、Tの本体、および式がenum型Tであるswitch文(14.11)のcaseラベルです。

名前は、enum定数に解決されるのではなく、enumクラスの暗黙的なフィールドに解決されます。switch文は一定の特別な処理を必要とするものの、通常のスコープ/名前解決メカニズムには依存しません。

メソッド(8.4.1)、コンストラクタ(8.8.1)またはラムダ式(15.27)の仮パラメータのスコープは、メソッド、コンストラクタまたはラムダ式の本体全体です。

クラスの型パラメータ(8.1.2)のスコープは、クラス宣言の型パラメータ・セクション、クラス宣言のスーパークラスまたはスーパーインタフェースの型パラメータ・セクション、およびクラス本体です。

インタフェースの型パラメータ(9.1.2)のスコープは、インタフェース宣言の型パラメータ・セクション、インタフェース宣言のスーパーインタフェースの型パラメータ・セクション、およびインタフェース本体です。

メソッドの型パラメータ(8.4.4)のスコープは、メソッドの宣言全体で、型パラメータ・セクションを含みますが、メソッド修飾子は除きます。

コンストラクタの型パラメータ(8.8.4)のスコープは、コンストラクタの宣言全体で、型パラメータ・セクションを含みますが、コンストラクタ修飾子は除きます。

ブロック(14.2)で直接囲まれたローカル・クラス宣言のスコープは、直接囲んでいるブロックの残りの部分であり、それ自体のクラス宣言を含みます。

switchブロック文グループ(14.11)で直接囲まれたローカル・クラス宣言のスコープは、直接囲んでいるswitchブロック文グループの残りの部分であり、それ自体のクラス宣言を含みます。

ブロックのローカル変数宣言(14.4)のスコープは、宣言が出現するブロックの残りの部分であり、それ自体のイニシャライザから始まり、ローカル変数宣言文の右側にある別の宣言子を含みます。

基本的なfor文(14.14.1)のForInit部分で宣言されたローカル変数のスコープには、次がすべて含まれます。

拡張されたfor文(14.14.2)のFormalParameter部分で宣言されたローカル変数のスコープが、含まれているです。

try文(14.20)のcatch句で宣言された例外ハンドラのパラメータのスコープは、catchに関連付けられたブロック全体です。

try-with-resources文(14.20.3)のResourceSpecificationで宣言された変数のスコープは、ResourceSpecificationの残りの部分の右側にある宣言、およびtry-with-resources文に関連付けられたtryブロック全体です。

try-with-resources文の翻訳は、前述のルールを示します。

例6.3-1.クラス宣言のスコープ

これらのルールは、クラスおよびインタフェースの宣言が型の使用の前に出現する必要がないことを示します。次のプログラムでは、クラス宣言PointListのスコープにクラスPointとクラスPointListの両方、およびパッケージpointsの他のコンパイル・ユニット内の他のクラスまたはインタフェース宣言が含まれるため、クラスPoint内のPointListの使用は有効です。

package points;
class Point {
    int x, y;
    PointList list;
    Point next;
}

class PointList {
    Point first;
}

例6.3-2.ローカル変数宣言のスコープ

ローカル変数xの初期化がローカル変数xの宣言のスコープ内にありますが、ローカル変数xはまだ値を持たず、使用できないため、次のプログラムを実行すると、コンパイル時にエラーが発生します。フィールドxは値0 (Test1が初期化されたときに割り当てられたもの)を持ちますが、ローカル変数xによってシャドウ化(6.4.1)されているため、注意がそらされることがあります。

class Test1 {
    static int x;
    public static void main(String[] args) {
        int x = x;
    }
}

次のプログラムは正常にコンパイルされます。

class Test2 {
    static int x;
    public static void main(String[] args) {
        int x = (x=2)*2;
        System.out.println(x);
    }
}

これは、ローカル変数xが使用される前に明確に割り当てられている(16)ためです。次のように出力されます。

4

次のプログラムでは、threeのイニシャライザは、前の宣言子で宣言された変数twoを正確に参照でき、次の行のメソッド呼出しは、ブロック内で前に宣言された変数threeを正確に参照できます。

class Test3 {
    public static void main(String[] args) {
        System.out.print("2+1=");
        int two = 2, three = two + 1;
        System.out.println(three);
    }
}

このプログラムでは、次の出力が生成されます。

2+1=3

6.5 名前の意味の確認

6.5.1 コンテキストに応じた名前の構文的分類

次のコンテキストでは、名前が構文的にModuleNameとして分類されます。

次のコンテキストでは、名前が構文的にPackageNameとして分類されます。

次のコンテキストでは、名前が構文的にTypeNameとして分類されます。

前述の16のコンテキスト内のReferenceTypeの識別子からのTypeNameの抽出は、要素型や型引数などのReferenceTypeのサブ用語すべてに繰り返し適用することを意図しています。

たとえば、フィールド宣言に型p.q.Foo[]が使用されるとします。配列型のカッコは無視され、用語p.q.Foo識別子の点線シーケンスとして配列型内のカッコの左側に抽出され、TypeNameとして分類されます。後のステップで、pqおよびFooのどれが型名またはパッケージ名であるかが確認されます。

別の例として、キャスト演算子に型p.q.Foo<? extends String>が使用されるとします。用語p.q.Foo識別子用語の点線シーケンスとして再度、今回はパラメータ化された型内の<の左側に抽出され、TypeNameとして分類されます。用語Stringが、パラメータ化された型のワイルドカード型引数のextends句内の識別子として抽出され、TypeNameとして分類されます。

次のコンテキストでは名前がExpressionNameとして構文的に分類されています。

次のコンテキストでは、名前が構文的にMethodNameとして分類されます。

次のコンテキストでは、名前が構文的にPackageOrTypeNameとして分類されます。

次のコンテキストでは、名前が構文的にAmbiguousNameとして分類されます。

構文的分類の結果、特定の種類のエンティティを式の特定の部分に制限できるようになります。

6.5.4 PackageOrTypeNameの意味

6.5.4.1 単純なPackageOrTypeName

PackageOrTypeNameQが有効なTypeIdentifierで、かつQという名前のクラス、インタフェースまたは型パラメータのスコープに出現する場合、PackageOrTypeNameTypeNameとして再分類されます。

そうでない場合、PackageOrTypeNamePackageNameとして再分類されます。PackageOrTypeNameの意味は、再分類された名前の意味です。

6.5.4.2 修飾されたPackageOrTypeName

修飾されたPackageOrTypeNameQ.Idという形式であるとすると、Idが有効なTypeIdentifierで、かつQが示すクラス、インタフェース、型パラメータまたはパッケージにIdという名前のメンバー・クラスまたはインタフェースがある場合、修飾されたPackageOrTypeName名はTypeNameとして再分類されます。

そうでない場合はPackageNameとして再分類されます。修飾されたPackageOrTypeNameの意味は、再分類された名前の意味です。

6.5.5 型名の意味

TypeNameとして分類された名前の意味は、次のように判別されます。

6.5.5.1 単純な型名

型名が単一のIdentifierで構成される場合、その識別子は、この名前を持つクラス、インタフェースまたは型パラメータの正確に1つの宣言のスコープ(6.3)に出現する必要があり、そうでない場合はコンパイル時にエラーが発生します。

型名の意味は、そのクラス、インタフェースまたは型パラメータです。

6.5.5.2 修飾された型名

型名がQ.Idという形式である場合、Qは、現在のモジュールから一意に見えるパッケージ内の型クラス、インタフェースまたは型パラメータの名前であるか、現在のモジュールから一意に見えるパッケージの名前である必要があります(7.4.3)。

包含クラス、インタフェースまたは型パラメータに解決可能な名前がある場合、そのパッケージが一意に見えるかどうか、さらにはパッケージのメンバーであるかどうか(たとえば、ローカル・クラスと型パラメータはパッケージのメンバーではありません)は、問題ではありません。

Idが、Qが示すクラス、インタフェース、型パラメータまたはパッケージのメンバーである、アクセス可能な正確に1つのクラスまたはインタフェース(6.6)の名前である場合、修飾された型名はそのクラスまたはインタフェースを示します。

Idが、Q内のメンバー・クラスまたはインタフェースの名前でない場合(8.59.5)、Q内のIdという名前のメンバー・クラスまたはインタフェースがアクセス可能でない場合(6.6)、あるいはIdQ内の複数のメンバー・クラスまたはインタフェースの名前である場合は、コンパイル時にエラーが発生します。

例6.5.5.2-1.修飾された型名

class Test {
    public static void main(String[] args) {
        java.util.Date date =
            new java.util.Date(System.currentTimeMillis());
        System.out.println(date.toLocaleString());
    }
}

このプログラムを最初に実行したときには、次の出力が生成されました。

Sun Jan 21 22:56:29 1996

この例では、名前java.util.Dateは型を示す必要があるため、最初にこの手順を再帰的に使用して、java.utilがアクセス可能なクラス、インタフェース、型パラメータまたはパッケージのいずれであるかを特定した後、型Dateがこのパッケージ内でアクセス可能であるかどうかを確認します。

6.6 アクセス制御

パッケージまたはクラスのユーザーが、そのパッケージまたはクラスの実装の不要な詳細に依存することを防ぐために、Javaプログラミング言語にはアクセス制御のメカニズムが用意されています。アクセスが許可される場合、アクセス対象のエンティティはアクセス可能であると言い表されます。

アクセス可能性は、コンパイル時に判断される静的プロパティです。これは、型および宣言の修飾子にのみ依存します。

修飾名は、パッケージ、クラス、インタフェース、型パラメータおよび参照型のメンバーにアクセスする手段です。そのようなメンバーの名前が、そのコンテキストから、修飾された型名(パッケージまたは参照型クラス、インタフェースまたは型パラメータのメンバー、6.5.5.2)または修飾された式名(参照型のメンバー、6.5.6.2)として分類される場合(6.5.1)、アクセス制御が適用されます。

たとえば、単一型インポート宣言では修飾された型名(7.5.1)を使用するため、名前付きクラスまたはインタフェースには、import宣言を含むコンパイル・ユニットからアクセスできる必要があります。別の例として、クラス宣言ではスーパークラス(8.1.5)に修飾された型名が使用される場合があるため、この場合も名前付きクラスにアクセスできる必要があります。

いくつかの明白な式が6.5.1のコンテキスト分類から「欠落」しています。Primaryでのフィールド・アクセス(15.11.1)、Primaryでのメソッド呼出し(15.12)、Primaryを介したメソッド参照(15.13)および修飾されたクラス・インスタンス作成でのインスタンス化されたクラス(15.9)です。これらの式はそれぞれ、6.2に示されている理由により、名前ではなく識別子を使用します。その結果、メンバー(フィールド、メソッドまたは型、クラスまたはインタフェース)に対するアクセス制御は、フィールド・アクセス式、メソッド呼出し式、メソッド参照式および修飾されたクラス・インスタンス作成式で明示的に適用されます。(フィールドに対するアクセスは、接尾辞式として出現する修飾名で示される場合もあります。)

また、多くの文と式で、型名ではなくのみで表されない型を使用できます。たとえば、クラス宣言では、パラメータ化された型(4.5)を使用してスーパークラスが示される場合があります。パラメータ化された型は修飾された型名ではないため、クラス宣言では、示されたスーパークラスに対してアクセス制御を明示的に実行する必要があります。その結果、TypeNameを分類するための6.5.1のコンテキストを提供するほとんどの文と式で、独自のアクセス制御チェックも実行されます。

パッケージまたは参照型クラス、インタフェースまたは型パラメータのメンバーへのアクセスに加えて、参照型クラスのコンストラクタへのアクセスを考慮する必要があります。コンストラクタが明示的または暗黙的に呼び出されたときに、アクセス制御をチェックする必要があります。その結果、アクセス制御は明示的なコンストラクタ呼出し文(8.8.7.1)とクラス・インスタンス作成式(15.9.3)によってチェックされます。このようなチェックが必要になるのは、6.5.1では明示的なコンストラクタ呼出し文の指定がなく(間接的にコンストラクタ名を参照するため)、非修飾クラス・インスタンス作成式で示されるクラスとそのクラスのコンストラクタの違いが考慮されていないためです。また、コンストラクタには修飾名がないため、修飾された型名の分類中にチェックされたアクセス制御を信頼することはできません。

アクセス可能性は、非表示やメソッドのオーバーライド(8.4.8.1)を含む、クラス・メンバーの継承(8.2)に影響します。

第7章: パッケージおよびモジュール

7.3 コンパイル・ユニット

CompilationUnitは、Javaプログラムの構文文法(2.3)の目標シンボル(2.1)です。これは、次のプロダクションによって定義されます。

CompilationUnit:
OrdinaryCompilationUnit
ModularCompilationUnit
OrdinaryCompilationUnit:
[PackageDeclaration] {ImportDeclaration} { TypeDeclaration TopLevelClassOrInterfaceDeclaration }
ModularCompilationUnit:
{ImportDeclaration} ModuleDeclaration

通常コンパイル・ユニットは3つの部分で構成され、それぞれがオプションです。

モジュラ・コンパイル・ユニットmodule宣言(7.7)で構成され、オプションで、その前にimport宣言が含められます。import宣言を使用すると、このモジュールおよび他のモジュールのパッケージ内のクラスおよびインタフェースクラスおよびインタフェースstaticメンバーを、module宣言内でそれらの単純名を使用して参照できます。

package宣言の直後の各コンパイル・ユニットの先頭に宣言import java.lang.*;が出現する場合と同様に、それぞれのコンパイル・ユニットによって、事前定義済のパッケージjava.langで宣言されたそれぞれのpublicクラスまたはインタフェース名が暗黙的にインポートされます。その結果、それらのクラスおよびインタフェースすべての名前をそれぞれのコンパイル・ユニットで単純名として使用できます。

ホスト・システムによって、観察可能なコンパイル・ユニットが特定されます。ただし、事前定義済のパッケージjavaとそのサブパッケージlangおよびio内のコンパイル・ユニットは除きます(これらはすべて、常に観察可能です)。

それぞれの観察可能なコンパイル・ユニットは、次のようにモジュールに関連付けることができます。

コンパイル・ユニットの観察可能性はそのパッケージの観察可能性(7.4.3)に影響し、観察可能なコンパイル・ユニットとモジュールとの関連付けはそのモジュールの観察可能性(7.7.6)に影響します。

モジュールMに関連付けられたモジュラ・コンパイル・ユニットおよび通常コンパイル・ユニットをコンパイルする場合、ホスト・システムは、Mの宣言で指定された依存を順守する必要があります。具体的には、ホスト・システムは、本来なら観察可能な通常コンパイル・ユニットを、Mから見えるもののみに制限する必要があります。Mから見える通常コンパイル・ユニットは、Mによって読み取られるモジュールに関連付けられた、観察可能な通常コンパイル・ユニットです。Mによって読み取られるモジュールは、java.lang.moduleパッケージ仕様に記載されているように、Mを唯一のルート・モジュールとする解決の結果によって指定されます。ホスト・システムは、解決を実行して、Mによって読み取られるモジュールを特定する必要があります。java.lang.moduleパッケージ仕様に記載されているいずれかの理由で解決が失敗すると、コンパイル時にエラーが発生します。

読取り可能性の関係は再帰的であるため、Mはそれ自体を読み取り、Mに関連付けられたモジュラ・コンパイル・ユニットおよび通常コンパイル・ユニットはすべて、Mから見えます。

Mによって読み取られるモジュールによって、Mから一意に見えるパッケージが決まり(7.4.3)、さらに、それによって、スコープ内の最上位パッケージと、Mに関連付けられたモジュラ・コンパイル・ユニットおよび通常コンパイル・ユニット内のコードのパッケージ名の意味の両方が決まります(6.36.5.36.5.5)。

前述のルールによって、モジュラ・コンパイル・ユニット内の注釈(特に、モジュール宣言に適用される注釈)で使用されるパッケージ/型の名前が、モジュールに関連付けられた通常コンパイル・ユニットにそれらが出現する場合と同様に解釈されることが確実になります。

異なる通常コンパイル・ユニットで宣言されたクラスおよびインタフェースは、相互を循環的に参照することがあります。Javaコンパイラは、そのようなすべてのクラスおよびインタフェースを同時にコンパイルするように準備する必要があります。

7.5 インポート宣言

インポート宣言を使用すると、名前付き型またはクラス、インタフェースまたはstaticメンバーを、単一の識別子で構成される単純名(6.2)で参照できます。

適切なインポート宣言を使用しない場合、別のパッケージで宣言されたクラスやインタフェース、または別のクラスまたはインタフェースstaticメンバーを参照するには、完全修飾名(6.7)を使用することが唯一の方法となります一般的に必要になります

元の記載に関する問題:

ImportDeclaration:
SingleTypeImportDeclaration
TypeImportOnDemandDeclaration
SingleStaticImportDeclaration
StaticImportOnDemandDeclaration

これらの宣言によってインポートされるクラス、インタフェースまたはメンバーのスコープおよびシャドウ化については、6.3および6.4に規定されています。

import宣言によって、実際にそのimport宣言を含むコンパイル・ユニット内でのみ、クラス、インタフェースまたはメンバーを単純名で使用できるようになります。import宣言によって導入されるクラス、インタフェースまたはメンバーのスコープには、具体的には、同じパッケージ内の他のコンパイル・ユニット、現在のコンパイル・ユニット内の他のimport宣言、または現在のコンパイル・ユニット内のpackage宣言(package宣言の注釈は除く)は含まれません。

7.5.1 単一型インポート宣言

単一型インポート宣言では、単一のクラスまたはインタフェースを、その正規名を指定することによってインポートし、その単一型インポート宣言が出現するコンパイル・ユニットのモジュール、クラスおよびインタフェース宣言内で単純名で使用できるようにします。

SingleTypeImportDeclaration:
import TypeName ;

TypeNameは、クラス型、インタフェース型、enum型または注釈型またはインタフェースの正規名(6.7)である必要があります。

クラスまたはインタフェースは、名前付きパッケージのメンバーであるか、最も外側にある字句的な包含クラスまたはインタフェース宣言(8.1.3)が名前付きパッケージのメンバーであるクラスまたはインタフェースのメンバーである必要があり、そうでない場合はコンパイル時にエラーが発生します。

名前付きクラスまたはインタフェースがアクセス可能でない場合(6.6)、コンパイル時にエラーが発生します。

同じコンパイル・ユニット内の2つの単一型インポート宣言が同じ単純名のクラスまたはインタフェースをインポートしようとすると、コンパイル時にエラーが発生します。ただし、2つのクラスまたはインタフェースが同じである場合は除きます(この場合、重複する宣言は無視されます)。

単一型インポート宣言によってインポートされるクラスまたはインタフェースが、import宣言を含むコンパイル・ユニット内で宣言される場合、import宣言は無視されます。

単純名がnであるクラスまたはインタフェースが単一型インポート宣言によってインポートされ、単純名がnである最上位クラスまたはインタフェース(7.6)もコンパイル・ユニットで宣言される場合、コンパイル時にエラーが発生します。

単純名がnであるクラスまたはインタフェースをインポートする単一型インポート宣言と、単純名がnであるクラスまたはインタフェースをインポートする単一静的インポート宣言(7.5.3)の両方がコンパイル・ユニットに含まれる場合、コンパイル時にエラーが発生します。ただし、2つのクラスまたはインタフェースが同じである場合は除きます(この場合、重複する宣言は無視されます)。

例7.5.1-1.単一型インポート

import java.util.Vector;

これにより、単純名Vectorがコンパイル・ユニット内のクラスおよびインタフェース宣言内で使用可能になります。つまり、単純名Vectorは、フィールド、パラメータまたはローカル変数の宣言や、同じ名前を持つネストしたクラスまたはインタフェース宣言によってシャドウ化(6.4.1)または不明瞭化(6.4.2)されていないすべての場所で、パッケージjava.util内のクラス宣言Vectorを参照します。

java.util.Vectorの実際の宣言は汎用です(8.1.2)。インポートされた後、名前Vectorは、Vector<String>などのパラメータ化された型内の修飾なしで、またはRAW型Vectorとして使用できます。import宣言に関連する制限として、汎用クラスまたはインタフェース宣言内で宣言されたネストした型メンバー・クラスまたはインタフェースはインポートできますが、その外側の型は常に消去されます。

例7.5.1-2.クラスまたはインタフェース宣言の重複

次のプログラムを実行すると、

import java.util.Vector;
class Vector { Object[] vec; }

次の場合と同様に、Vectorの宣言が重複するため、コンパイル時にエラーが発生します。

import java.util.Vector;
import myVector.Vector;

この場合、myVectorは、コンパイル・ユニットが含まれるパッケージです。

package myVector;
public class Vector { Object[] vec; }

例7.5.1-3.サブパッケージのインポートなし

import宣言でサブパッケージをインポートすることはできません。インポートできるのは、クラスまたはインタフェースのみです。

たとえば、java.utilをインポートし、名前util.Randomを使用して型java.util.Randomを参照しようとしても動作しません。

import java.util;
class Test { util.Random generator; }
  // incorrect: compile-time error

例7.5.1-4.パッケージ名でもある型名のインポート

パッケージ名および型名は通常、6.1で説明されている命名規則では異なるものです。ただし、人為的な例として次を想定します。ここで使用されるのは、Mosquitoという名前のパブリック・クラスを宣言する、慣例に従わない名前が付けられたパッケージVector:

package Vector;
public class Mosquito { int capacity; }

および次のコンパイル・ユニットです。

package strange;
import java.util.Vector;
import Vector.Mosquito;
class Test {
    public static void main(String[] args) {
        System.out.println(new Vector().getClass());
        System.out.println(new Mosquito().getClass());
    }
}

クラスVectorをパッケージjava.utilからインポートする単一型インポート宣言では、パッケージ名Vectorが後続のimport宣言に出現して適切に認識されることが阻止されることはありません。この例では、次の出力をコンパイルして生成しています。

class java.util.Vector
class Vector.Mosquito

7.5.2 オンデマンド型インポート宣言

オンデマンド型インポート宣言を使用すると、名前付きパッケージ、クラスまたはインタフェースまたは型のアクセス可能なすべてのクラスおよびインタフェースを必要に応じてインポートできます。

TypeImportOnDemandDeclaration:
import PackageOrTypeName . * ;

PackageOrTypeNameは、パッケージ、クラスまたはインタフェース型、enum型または注釈型の正規名(6.7)である必要があります。

PackageOrTypeNameクラスまたはインタフェースを示す場合(6.5.4)、そのクラスまたはインタフェースは、名前付きパッケージのメンバーであるか、最も外側にある字句的な包含クラスまたはインタフェース宣言(8.1.3)が名前付きパッケージのメンバーであるクラスまたはインタフェースのメンバーである必要があり、そうでない場合はコンパイル時にエラーが発生します。

名前付きパッケージが現在のモジュールから一意に見えない場合(7.4.3)、または名前付きクラスまたはインタフェースがアクセス可能でない場合(6.6)、コンパイル時にエラーが発生します。

オンデマンド型インポート宣言で現在のコンパイル・ユニットの名前付きパッケージまたはjava.langのいずれかに名前を付ける場合、コンパイル時エラーにはなりません。このような場合、オンデマンド型インポート宣言は無視されます。

同じコンパイル・ユニット内の2つ以上のオンデマンド型インポート宣言によって、同じ型またはパッケージ、クラスまたはインタフェースの名前が指定されることがあります。これらの宣言の1つを除いてすべてが重複しているとみなされます。この結果、その型は1回のみインポートされたものとして扱われます。

同じクラスまたはインタフェースの名前を指定するオンデマンド型インポート宣言とオンデマンド静的インポート宣言(7.5.4)の両方がコンパイル・ユニットに含まれる場合、そのクラスまたはインタフェースstaticメンバー・クラスおよびインタフェース(8.59.5)が1回のみインポートされた場合と同様の結果になります。

例7.5.2-1.オンデマンド型インポート

import java.util.*;

これにより、パッケージjava.util内で宣言されたすべてのpublicクラスおよびインタフェースの単純名がコンパイル・ユニットのクラスおよびインタフェース宣言内で使用可能になります。つまり、単純名Vectorは、クラスVectorの宣言がシャドウ化(6.4.1)または不明瞭化(6.4.2)されていないコンパイル・ユニット内のすべての場所でパッケージjava.util内のそのクラスを参照します。

この宣言は、単純名がVectorであるクラスまたはインタフェースの単一型インポート宣言によって、コンパイル・ユニットが属するパッケージ内で宣言されたVectorという名前のクラスまたはインタフェースによって、あるいはネストしたクラスまたはインタフェースによってシャドウ化される場合があります。

この宣言は、フィールド、パラメータ、またはVectorという名前のローカル変数の宣言によって不明瞭化される場合があります。

(これらのいずれかの状況が発生するのは異常です。)

7.5.3 単一静的インポート宣言

単一静的インポート宣言では、特定の単純名を持つアクセス可能なすべてのstaticメンバーをクラスまたはインタフェースからインポートします。これにより、単一静的インポート宣言が出現するコンパイル・ユニットのモジュール、クラスおよびインタフェース宣言内でstaticメンバーを単一名で使用できるようになります。

SingleStaticImportDeclaration:
import static TypeName . Identifier ;

TypeNameは、クラス型、インタフェース型、enum型または注釈型またはインタフェースの正規名(6.7)である必要があります。

クラスまたはインタフェースは、名前付きパッケージのメンバーであるか、最も外側にある字句的な包含クラスまたはインタフェース宣言(8.1.3)が名前付きパッケージのメンバーであるクラスまたはインタフェースのメンバーである必要があり、そうでない場合はコンパイル時にエラーが発生します。

名前付きクラスまたはインタフェースがアクセス可能でない場合(6.6)、コンパイル時にエラーが発生します。

Identifierは、名前付きクラスまたはインタフェースの少なくとも1つのstaticメンバーの名前である必要があります。その名前を持つstaticメンバーがないか、名前付きメンバーがすべてアクセス不可能である場合は、コンパイル時にエラーが発生します。

1つの単一静的インポート宣言で、同じ名前を持つ複数のフィールド、クラスまたはインタフェースまたは型、あるいは同じ名前とシグネチャを持つ複数のメソッドをインポートできます。このことが発生するのは、名前付きクラスまたはインタフェースが、すべて同じ名前を持つ複数のフィールド、メンバー・クラス、メンバー・インタフェースまたはメソッドをそれ自体のスーパー・タイプから継承する場合です。

同じコンパイル・ユニット内の2つの単一静的インポート宣言が同じ単純名のクラスまたはインタフェースをインポートしようとすると、コンパイル時にエラーが発生します。ただし、2つのクラスまたはインタフェースが同じである場合は除きます(この場合、重複する宣言は無視されます)。

単純名がnであるクラスまたはインタフェースが単一静的インポート宣言によってインポートされ、単純名がnである最上位クラスまたはインタフェース(7.6)もコンパイル・ユニットで宣言される場合、コンパイル時にエラーが発生します。

単純名がnであるクラスまたはインタフェースをインポートする単一静的インポート宣言と、単純名がnであるクラスまたはインタフェースをインポートする単一型インポート宣言(7.5.1)の両方がコンパイル・ユニットに含まれる場合、コンパイル時にエラーが発生します。ただし、2つのクラスまたはインタフェースが同じである場合は除きます(この場合、重複する宣言は無視されます)。

7.5.4 オンデマンド静的インポート宣言

オンデマンド静的インポート宣言を使用すると、名前付きクラスまたはインタフェースのアクセス可能なすべてのstaticメンバーを必要に応じてインポートできます。

StaticImportOnDemandDeclaration:
import static TypeName . * ;

TypeNameは、クラス型、インタフェース型、enum型または注釈型またはインタフェースの正規名(6.7)である必要があります。

クラスまたはインタフェースは、名前付きパッケージのメンバーであるか、最も外側にある字句的な包含クラスまたはインタフェース宣言(8.1.3)が名前付きパッケージのメンバーであるクラスまたはインタフェースのメンバーである必要があり、そうでない場合はコンパイル時にエラーが発生します。

名前付きクラスまたはインタフェースがアクセス可能でない場合(6.6)、コンパイル時にエラーが発生します。

同じコンパイル・ユニット内の2つ以上のオンデマンド静的インポート宣言によって、同じクラスまたはインタフェースの名前が指定されることがあります。この場合、そのような宣言が1つのみ存在する場合と同様の結果になります。

同じコンパイル・ユニット内に複数のオンデマンド静的インポート宣言がある場合、同じメンバーの名前が付けられる場合があります。この結果、メンバーが正確に1回インポートされたものとして扱われます。

1つのオンデマンド静的インポート宣言で、同じ名前を持つ複数のフィールド、クラスまたはインタフェースまたは型、あるいは同じ名前とシグネチャを持つ複数のメソッドをインポートできます。このことが発生するのは、名前付きクラスまたはインタフェースが、すべて同じ名前を持つ複数のフィールド、メンバー・クラス、メンバー・インタフェースまたはメソッドをそれ自体のスーパー・タイプから継承する場合です。

同じクラスまたはインタフェースの名前を指定するオンデマンド静的インポート宣言とオンデマンド型インポート宣言(7.5.2)の両方がコンパイル・ユニットに含まれる場合、そのクラスまたはインタフェースstaticメンバー・クラスおよびインタフェース(8.59.5)が1回のみインポートされた場合と同様の結果になります。

7.6 最上位クラスおよびインタフェース宣言

最上位クラスまたはインタフェース宣言では、最上位クラス(88.1)または最上位インタフェース(99.1)を宣言します。

TypeDeclaration: TopLevelClassOrInterfaceDeclaration:
ClassDeclaration
InterfaceDeclaration
;

コンパイル・ユニット内でクラスまたはインタフェース宣言のレベルに出現する余分な;トークンは、コンパイル・ユニットの意味に影響しません。Javaプログラミング言語では、不要なセミコロンは、クラス宣言の後に;を配置することに慣れているC++プログラマのための便宜的措置としてのみ許容されています。新しいJavaコードでは使用しないでください。

アクセス修飾子がない場合、最上位クラスまたはインタフェースはパッケージ・アクセス権を持ちます。それが宣言されたパッケージの通常コンパイル・ユニット内でのみアクセス可能です(6.6.1)。クラスまたはインタフェースpublicとして宣言すると、同じモジュールの他のパッケージのコードから、また場合によっては他のモジュールのパッケージのコードからクラスまたはインタフェースへのアクセスを許可できます。

protectedprivateまたはstaticのいずれかのアクセス修飾子が最上位クラスまたはインタフェース宣言に含まれている場合は、コンパイル時にエラーが発生します。

最上位クラスまたはインタフェースの名前が、同じパッケージで宣言された他の最上位クラスまたはインタフェースの名前として出現する場合は、コンパイル時にエラーが発生します。

最上位クラスまたはインタフェースのスコープおよびシャドウ化については、6.3および6.4に規定されています。

最上位クラスまたはインタフェースの完全修飾名については、6.7に規定されています。

例7.6-1.最上位クラスまたはインタフェース宣言の競合

package test;
import java.util.Vector;
class Point {
    int x, y;
}
interface Point {  // compile-time error #1
    int getR();
    int getTheta();
}
class Vector { Point[] pts; }  // compile-time error #2

ここでは、最初のコンパイル時エラーの原因は、名前Pointを同じパッケージ内でクラスとインタフェースの両方として重複して宣言していることです。2つ目のコンパイル時エラーでは、クラス宣言と単一型インポート宣言の両方によって名前Vectorを宣言しようとしています。

ただし、クラスの名前が、そのクラス宣言を含むコンパイル・ユニット(7.3)内のオンデマンド型インポート宣言(7.5.2)によって別にインポートされる可能性があるクラスまたはインタフェースの名前でもある場合には、エラーにはなりません。つまり、次のプログラムでは、

package test;
import java.util.*;
class Vector {}  // not a compile-time error

クラスVectorの宣言は、クラスjava.util.Vectorも存在するとしても許可されます。このコンパイル・ユニット内では、単純名Vectorはクラスtest.Vectorを参照し、java.util.Vectorを参照するわけではありません(コンパイル・ユニット内のコードによってこれを参照することはできますが、完全修飾名によってのみ可能です)。

例7.6-2.最上位クラスおよびインタフェースのスコープ

package points;
class Point {
    int x, y;           // coordinates
    PointColor color;   // color of this point
    Point next;         // next point with this color
    static int nPoints;
}
class PointColor {
    Point first;        // first point with this color
    PointColor(int color) { this.color = color; }
    private int color;  // color components
}

このプログラムでは、クラス・メンバーの宣言で相互を使用する2つのクラスが定義されます。クラス型クラスPointおよびPointColorは、パッケージpoints内のすべてのクラス宣言(現在のコンパイル・ユニット内のものすべてを含む)をスコープとして持つため、このプログラムは正しくコンパイルされます。つまり、前方参照は問題ではありません。

例7.6-3.完全修飾名

class Point { int x, y; }

このコードでは、package宣言がないコンパイル・ユニット内でクラスPointが宣言されているため、Pointはその完全修飾名です。一方、次のコードでは、

package vista;
class Point { int x, y; }

クラスPointの完全修飾名はvista.Pointです。(パッケージ名vistaは、ローカルまたは個人での使用には適しています。パッケージを広範に配布する予定がある場合は、一意のパッケージ名を付けることをお薦めします(6.1)。)

Java SEプラットフォームの実装では、パッケージ内のクラスおよびインタフェースを、それらの包含モジュール名とバイナリ名(13.1)の組合せによって追跡する必要があります。クラスまたはインタフェースの複数の命名方法をバイナリ名に拡張して、そのような名前が同じクラスまたはインタフェースを参照するものとして確実に理解されるようにする必要があります。

たとえば、コンパイル・ユニットに次の単一型インポート宣言(7.5.1)が含まれているとします。

import java.util.Vector;

この場合、そのコンパイル・ユニット内では、単純名Vectorと完全修飾名java.util.Vectorは同じクラスを参照します。

パッケージがファイル・システムに格納されている場合(7.2)にのみ、ホスト・システムは、次のいずれかに該当し、かつ、クラスまたはインタフェースが、そのクラスまたはインタフェース名と拡張子(.java.javなど)で構成される名前のファイルに見つからない場合はコンパイル時にエラーが発生するという制限を強制することを選択できます。

この制限は、1つのコンパイル・ユニットについてそのようなクラスまたはインタフェースを1つまでにする必要があることを意味します。この制限によって、Javaコンパイラはパッケージ内で名前付きクラスまたはインタフェースを容易に見つけることができます。実際には、多くのプログラマは、各クラスまたはインタフェースpublicであるかどうかや、他のコンパイル・ユニットのコードによって参照されているかどうかに関係なく、クラスまたはインタフェースをその独自のコンパイル・ユニットに配置します。

たとえば、publicwet.sprocket.Toadのソース・コードはディレクトリwet/sprocketのファイルToad.java内にあり、対応するオブジェクト・コードは同じディレクトリのファイルToad.class内にあります。

7.7 モジュール宣言

7.7.3 サービス消費

usesディレクティブは、現在のモジュールがjava.util.ServiceLoaderを介してプロバイダを検出できるサービスを指定します。

サービスはクラス型、インタフェース型または注釈型である必要があります。usesディレクティブでenumクラス(8.9)がサービスとして指定される場合、コンパイル時にエラーが発生します。

解決可能なTypeNameとして、サービスはクラスまたはインタフェースになります。ここでの他の唯一の(若干変則的な)制約は、enumクラス以外である必要があるという点です。

サービスは、現在のモジュール内でも別のモジュール内でも宣言できます。サービスが現在のモジュール内で宣言されていない場合、現在のモジュール内のコードからサービスにアクセスできる必要があり(6.6)、そうでない場合はコンパイル時にエラーが発生します。

モジュール宣言の複数のusesディレクティブで同じサービスが指定される場合、コンパイル時にエラーが発生します。

7.7.4 サービスのプロビジョニング

providesディレクティブでサービスを指定し、with句でjava.util.ServiceLoaderに対する1つ以上のサービス・プロバイダを指定します。

サービスはクラス型、インタフェース型または注釈型である必要があります。providesディレクティブでenumクラス(8.9)をサービスとして指定した場合は、コンパイル時にエラーが発生します。

7.7.3のコメントを参照してください。

サービスは、現在のモジュール内でも別のモジュール内でも宣言できます。サービスが現在のモジュール内で宣言されていない場合、現在のモジュール内のコードからサービスにアクセスできる必要があり(6.6)、そうでない場合はコンパイル時にエラーが発生します。

それぞれのサービス・プロバイダは、publicで、かつ最上位であるか、ネストしたstaticであるpublicクラスまたはインタフェースである必要があり、そうでない場合はコンパイル時にエラーが発生します。

「ネストしたstatic」が意味するものが後であいまいになります。「ネストした」という単語を単に取り除くことによって、それを解決できます。正常に参照できるstaticクラスまたはインタフェースで問題ありません。

それぞれのサービス・プロバイダは現在のモジュール内で宣言する必要があります。そうしないとコンパイル時にエラーが発生します。

サービス・プロバイダが、仮パラメータがないpublicコンストラクタを明示的に宣言するか、publicなデフォルト・コンストラクタ(8.8.9)を暗黙的に宣言する場合、そのコンストラクタはプロバイダ・コンストラクタと呼ばれます。

サービス・プロバイダが、仮パラメータがないproviderというpublicstaticメソッドを明示的に宣言する場合、そのメソッドはプロバイダ・メソッドと呼ばれます。

サービス・プロバイダにプロバイダ・メソッドがある場合、その戻り型は、(i)現在のモジュール内で宣言されているか、別のモジュール内で宣言されていて、現在のモジュール内のコードからアクセス可能であり、かつ(ii) providesディレクティブで指定されたサービスのサブタイプである必要があります。そうでない場合はコンパイル時にエラーが発生します。

providesディレクティブによって指定されるサービス・プロバイダは現在のモジュール内で宣言する必要がありますが、そのプロバイダ・メソッドは、別のモジュール内で宣言された戻り型を持つことができます。また、サービス・プロバイダがプロバイダ・メソッドを宣言する場合、サービス・プロバイダ自体がサービスのサブタイプである必要はありません。

サービス・プロバイダにプロバイダ・メソッドがない場合、そのサービス・プロバイダはプロバイダ・コンストラクタを持つ必要があるとともに、providesディレクティブで指定されたサービスのサブタイプである必要があります。そうでない場合はコンパイル時にエラーが発生します。

モジュール宣言内の複数のprovidesディレクティブで同じサービスを指定した場合、コンパイル時にエラーが発生します。

特定のprovidesディレクティブのwith句で同じサービス・プロバイダを複数回指定した場合、コンパイル時にエラーが発生します。

第8章: クラス

クラス宣言では、新しい参照型クラスを定義し、これらの実装方法について記述します(8.1)。

最上位クラス(7.6)は、ネストしたクラスではないコンパイル・ユニットの最上位で宣言されるクラスです。

ネストしたクラスは、宣言が別のクラスまたはインタフェースの本体内で行われるクラスです。ネストしたクラスは、メンバー・クラス(8.59.5)、ローカル・クラス(14.3)または無名クラス(15.9.5)である場合があります。

内部クラス(8.1.3)は、包含クラス・インスタンス、ローカル変数および型変数を参照できる、ネストしたクラスです。

enumクラス(8.9)は、名前付きクラス・インスタンスの小規模なセットを定義する特別な構文で宣言されるクラスです。

この章では、すべてのクラス、つまり、最上位クラス(7.6)とネストしたクラス(メンバー・クラス(8.59.5)、ローカル・クラス(14.3)および無名クラス(15.9.5)を含む)の共通セマンティクスについて説明します。特定の種類のクラスに固有の詳細は、これらのコンストラクトに特化したセクションで説明します。

名前付きクラスはabstract (8.1.1.1)として宣言でき、これが不完全に実装されている場合は抽象であると宣言する必要があります。このようなクラスはインスタンス化できませんが、サブクラスによって拡張できます。クラスはfinal (8.1.1.2)として宣言される場合があります。この場合、このクラスはサブクラスを持つことができません。クラスがpublicとして宣言されている場合、そのモジュールの任意のパッケージ内のコードから、および場合によっては他のモジュール内のコードから参照できます。クラスでは、アクセス制御(6.6)を使用して、他のクラス、インタフェース、パッケージまたはモジュールからのクラスへの参照を防止できます。Objectを除く各クラスは、既存の単一のクラスの拡張(つまり、サブクラス) (8.1.4)であり、インタフェースを実装する場合があります(8.1.5)。クラスは汎用(8.1.2)である場合があります。つまり、クラスは、クラスの様々なインスタンス間でバインディングが異なる可能性のある型変数を宣言できます。

クラス宣言の完全なドメインをより適切に反映するための説明。

クラスは、他の種類の宣言と同じように、注釈(9.7)を使用して修飾できます。

クラスの本体では、メンバー(フィールドおよびメソッドおよびネストしたクラスおよびインタフェース)、インスタンス・イニシャライザ、静的イニシャライザおよびコンストラクタ(8.1.6)を宣言します。メンバー(8.2)のスコープ(6.3)は、メンバーが属するクラスの宣言の本体全体です。フィールド、メソッド、メンバー・クラス、メンバー・インタフェースおよびコンストラクタ宣言には、アクセス修飾子(6.6) publicprotectedまたはprivateが含まれる場合があります。クラスのメンバーには、宣言されたメンバーと継承されたメンバーの両方が含まれます(8.2)。新しく宣言されたフィールドは、スーパークラスまたはスーパーインタフェース内で宣言されたフィールドを隠すことができます。新しく宣言されたクラス・メンバーおよびインタフェース・メンバーメンバー・クラスおよびメンバー・インタフェースは、スーパークラスまたはスーパーインタフェース内で宣言されたクラス・メンバーまたはインタフェース・メンバーメンバー・クラスおよびメンバー・インタフェースを隠すことができます。新しく宣言されたメソッドは、スーパークラスまたはスーパーインタフェース内で宣言されたメソッドを隠す、実装する、またはオーバーライドすることができます。

フィールド宣言(8.3)では、1回インカネーションされるクラス変数、およびクラスのインスタンスごとに新しくインカネーションされるインスタンス変数を記述します。フィールドはfinal (8.3.1.2)として宣言できます。この場合、このフィールドは1回のみ割り当てることができます。任意のフィールド宣言にイニシャライザを含めることができます。

メンバー・クラス宣言(8.5)では、前後のクラスのメンバーであるネストしたクラスを記述します。メンバー・クラスはstaticである場合があります。この場合、メンバー・クラスは、前後のクラスのインスタンス変数にアクセスできません。または、内部クラス(8.1.3)である場合もあります。

以前、内部クラスが導入されたことで、これらの詳細が冗長になりました。

メンバー・インタフェース宣言(8.5)では、前後のクラスのメンバーであるネストしたインタフェースを記述します。

メソッド宣言(8.4)では、メソッド呼出し式(15.12)によって呼び出すことのできるコードを記述します。クラス・メソッドは、クラスに関連して呼び出されます。インスタンス・メソッドは、クラスのインスタンスである特定のオブジェクトに関連して呼び出されます。宣言が実装方法を示していないメソッドは、abstractとして宣言する必要があります。メソッドはfinal (8.4.3.3)として宣言される場合があります。この場合、このメソッドは隠したりオーバーライドすることができません。メソッドは、プラットフォーム依存のnativeコード(8.4.3.4)によって実装される場合があります。synchronizedメソッド(8.4.3.6)は、synchronized文(14.19)で使用されるように、本体を実行する前にオブジェクトを自動的にロックし、戻り時にオブジェクトを自動的にロック解除します。これにより、そのアクティビティを他のスレッド(17)のアクティビティと同期できるようになります。

メソッド名はオーバーロードする場合があります(8.4.9)。

インスタンス・イニシャライザ(8.6)は、作成時にインスタンスの初期化をサポートするために使用できる実行可能コードのブロックです(15.9)。

静的イニシャライザ(8.7)は、クラスの初期化をサポートするために使用できる実行可能コードのブロックです。

コンストラクタ(8.8)はメソッドと似ていますが、メソッド呼出しによって直接呼び出すことはできません。コンストラクタは、新しいクラス・インスタンスを初期化するために使用されます。メソッドと同様、これはオーバーロードする場合があります(8.8.8)。

8.1 クラス宣言

クラス宣言では、新しい名前付き参照型新しいクラスを指定します。

標準クラス宣言enum宣言という2種類のクラス宣言があります。

ClassDeclaration:
NormalClassDeclaration
EnumDeclaration
NormalClassDeclaration:
{ClassModifier} class TypeIdentifier [TypeParameters]
[Superclass ClassExtends] [Superinterfaces ClassImplements] ClassBody

プロダクションにはSuperclassTypeおよびSuperinterfaceTypesの名前を付けることができますが、使用するキーワードを強調する方が適切です(Throwsプロダクションを比較します)。

また、クラスは、ClassInstanceCreationExpression (15.9.5)またはクラス本体で終わるEnumConstant (8.9.1)によって暗黙的に宣言されます。

このセクションのルールは、enum宣言を含め、すべてのクラス宣言に適用されます。ただし、クラス修飾子、内部クラスおよびスーパークラスに関してはenum宣言に特別なルールが適用されます。これらのルールについては、8.9を参照してください。

クラスに関する個々の宣言形式(enum宣言および無名クラス宣言を含む)が第8章全体で規定されているルールにどのように関連するかについての説明は、すでに8に記載されています。ここでそれを繰り返すと混乱が生じます。

クラス宣言内のTypeIdentifierは、クラスの名前を指定します。

クラスの単純名が前後のクラスまたはインタフェースの単純名と同じである場合は、コンパイル時にエラーが発生します。

クラス宣言のスコープおよびシャドウ化については、6.3および6.4に規定されています。

8.1.1 クラス修飾子

クラス宣言には、クラス修飾子を含めることができます。

ClassModifier:
(次のうちの1つ)
Annotation public protected private
abstract static final strictfp

クラス宣言の注釈修飾子に関するルールについては、9.7.4および9.7.5に規定されています。

アクセス修飾子public (6.6)は、最上位クラス(7.6)とメンバー・クラス(8.59.5)にのみ関連します。ローカル・クラス(14.3)または匿名クラス(15.9.5)には関連しません。

アクセス修飾子protectedおよびprivateおよびstaticは、直接包含するクラス宣言(8.5)内のメンバー・クラスにのみ関連します。

修飾子staticは、メンバー・クラス(8.5.1)にのみ関連し、最上位クラス、ローカル・クラスまたは匿名クラスには関連しません。

これは包括的なリストまたはルールの場所ではありません。これらの文の目的は、特定の修飾子の使用方法を知る必要があるリーダーに、いくつかの相互参照を提供することです。そこで詳細を確認できます。

クラス宣言について同じキーワードが修飾子として複数回出現する場合、またはアクセス修飾子publicprotectedおよびprivate (6.6)のうちの複数がクラス宣言に含まれる場合、コンパイル時にエラーが発生します。

2つ以上の(別個の)クラス修飾子が1つのクラス宣言に出現する場合は、ClassModifierのプロダクションでの出現順序が前述の内容と一致することが慣例ですが、必須ではありません。

8.1.1.3 strictfpクラス

strictfp修飾子には、クラス宣言内(変数イニシャライザ、インスタンス・イニシャライザ、静的イニシャライザおよびコンストラクタ内を含む)のすべてのfloatまたはdouble式を明示的にFP-strict (15.4)にするという効果があります。

これは、そのクラスで宣言されるすべてのメソッドと、そのクラスで宣言されるすべてのネストしたクラスおよびインタフェースは暗黙的にstrictfpであることを意味します。

8.1.3 内部クラスと包含インスタンス

内部クラスは、明示的にも暗黙的にもstaticとして宣言されていない、ネストしたクラスです。

内部クラスは、非staticメンバー・クラス(8.5)、ローカル・クラス(14.3)または無名クラス(15.9.5)である場合があります。インタフェースのメンバー・クラスは暗黙的にstaticであるため(9.5)、内部クラスとみなされることはありません。

内部クラスで静的イニシャライザ(8.7)を宣言した場合、コンパイル時にエラーが発生します。

明示的または暗黙的にstaticであるメンバーを内部クラスで宣言した場合、コンパイル時にエラーが発生します。ただし、そのメンバーが定数変数(4.12.4)である場合は除きます。

内部クラスは、定数変数でないstaticメンバーを宣言することはできませんが、それらを継承することがあります。

内部クラスでないネストしたクラスでは、Javaプログラミング言語の通常のルールに従って、staticメンバーを自由に宣言できます。

例8.1.3-1.内部クラス宣言とstaticメンバー

class HasStatic {
    static int j = 100;
}
class Outer {
    class Inner extends HasStatic {
        static final int x = 3;  // OK: constant variable
        static int y = 4;  // Compile-time error: an inner class
    }
    static class NestedButNotInner{
        static int z = 5;    // OK: not an inner class
    }
    interface NeverInner {}  // Interfaces are never inner
}

文または式が静的コンテキストに出現するのは、その文または式を包含する最も内側のメソッド、コンストラクタ、インスタンス・イニシャライザ、静的イニシャライザ、フィールド・イニシャライザまたは明示的なコンストラクタ呼出し文が静的メソッド、静的イニシャライザ、静的変数の変数イニシャライザ、または明示的なコンストラクタ呼出し文(8.8.7.1)である場合のみです。

内部クラスCクラスまたはインタフェースOの直接内部クラスとなるのは、Oが、Cを直接包含するクラスまたはインタフェース宣言で、かつCの宣言が静的コンテキストに出現しない場合です。

内部クラスがローカル・クラスまたは無名クラスである場合は静的コンテキストで宣言でき、その場合、包含クラスまたはインタフェースの内部クラスとみなされることはありません。

クラスCクラスまたはインタフェースOの内部クラスとなるのは、それがOの直接内部クラスであるか、Oの内部クラスの内部クラスである場合です。

内部クラスを直接包含するクラスまたはインタフェース宣言がインタフェースであることはまれですが、その可能性はあります。このような状況が発生するのは、そのクラスが、デフォルトまたは静的メソッド本体(9.4)で宣言されたローカル・クラスまたは無名クラスである場合のみです。具体的には、デフォルト・メソッド本体で無名クラスまたはローカル・クラスが宣言されるか、デフォルト・メソッド本体で宣言された無名クラスの本体でメンバー・クラスが宣言されると、このような状況が発生します。

その2つ目の例では、直接包含するクラスまたはインタフェース宣言はインタフェースではなく、匿名クラスです。

クラスまたはインタフェースOは、それ自体のゼロ番目の字句的な包含型宣言です。

クラスOクラスCのn番目の字句的な包含型宣言となるのは、それがCn-1番目の字句的な包含型宣言を直接包含する型宣言である場合です。

クラスまたはインタフェースOの直接内部クラスCのインスタンスiは、iを直接包含するインスタンスと呼ばれる、Oのインスタンスに関連付けられます。オブジェクトを直接包含するインスタンス(ある場合)は、オブジェクトの作成時に特定されます(15.9.2)。

オブジェクトoは、それ自体のゼロ番目の字句的な包含インスタンスです。

オブジェクトoインスタンスiのn番目の字句的な包含インスタンスとなるのは、それがin-1番目の字句的な包含インスタンスを直接包含するインスタンスである場合です。

宣言が静的コンテキストに出現する内部クラスIローカル・クラスまたは無名クラスのインスタンスには、字句的な包含インスタンスはありません。ただし、Iが静的メソッドまたは静的イニシャライザ内で直接宣言されている場合、Iには、Iの宣言を字句的に包含する最も内側のブロック文である包含ブロックがあります。

「包含ブロック」という用語は、この段落外では使用されていません。このセクションにはすでに圧倒的な量の新しい用語が含まれているため、この追加定義はない方がよいです。

それ自体がクラスまたはインタフェースSOの直接内部クラスであるCのそれぞれのスーパークラスSについては、Sに関してiを直接包含するインスタンスと呼ばれる、iに関連付けられたSOのインスタンスがあります。そのクラス直接スーパークラス(ある場合)に関してオブジェクトを直接包含するインスタンスは、明示的なコンストラクタ呼出し文(8.8.7.1)を介してスーパークラス・コンストラクタが呼び出されるときに特定されます。

内部クラス(宣言が静的コンテキストに出現しないもの)が、字句的な包含クラスまたはインタフェース宣言のメンバーであるインスタンス変数を参照する場合、対応する字句的な包含インスタンスの変数が使用されます。

内部クラスで使用されるが、宣言されていないローカル変数、仮パラメータまたは例外パラメータは、finalとして宣言するか、実質的にfinalである(4.12.4)必要があります。そうでない場合は、その使用が試行されると、コンパイル時にエラーが発生します。

内部クラスで使用されるが、宣言されていないローカル変数は、内部クラスの本体より前で明確に割り当てる必要があります(16)。そうしないとコンパイル時にエラーが発生します。

変数の使用に関する同様のルールがラムダ式(15.27.2)の本体にも適用されます。

字句的な包含クラスまたはインタフェース宣言の空白のfinalフィールド(4.12.4)を内部クラス内で割り当てることはできません。そのようにすると、コンパイル時にエラーが発生します。

例8.1.3-2.内部クラス宣言

class Outer {
    int i = 100;
    static void classMethod() {
        final int l = 200;
        class LocalInStaticContext {
            int k = i;  // Compile-time error
            int m = l;  // OK
        }
    }
    void foo() {
        class Local {  // A local class
            int j = i;
        }
    }
}

クラスLocalInStaticContextの宣言は、静的メソッドclassMethod内にあるため、静的コンテキストに出現します。クラスOuterのインスタンス変数は、静的メソッドの本体内では使用できません。具体的には、Outerのインスタンス変数をLocalInStaticContextの本体内で使用することはできません。ただし、前後のメソッドからのローカル変数はエラーなしで参照できます(それらがfinalとして宣言されているか、実質的にfinalである場合)。

宣言が静的コンテキストに出現しない内部クラスは、それらの包含クラス宣言のインスタンス変数を自由に参照できます。インスタンス変数は常にインスタンスに関して定義されます。包含クラス宣言のインスタンス変数については、その宣言された型内部クラスの包含インスタンスに関してインスタンス変数を定義する必要があります。たとえば、前述のクラスLocalには、クラスOuterの包含インスタンスがあります。さらに、次のような例があるとします。

class WithDeepNesting {
    boolean toBe;
    WithDeepNesting(boolean b) { toBe = b; }

    class Nested {
        boolean theQuestion;
        class DeeplyNested {
            DeeplyNested(){
                theQuestion = toBe || !toBe;
            }
        }
    }
}

ここでは、WithDeepNesting.Nested.DeeplyNestedのそれぞれのインスタンスに、クラスWithDeepNesting.Nestedの包含インスタンス(それを直接包含するインスタンス)とクラスWithDeepNestingの包含インスタンス(その2番目の字句的な包含インスタンス)があります。

8.1.4 スーパークラスおよびサブクラス

ここで説明するコア機能は、クラスのスーパークラスです。「サブクラス」は単なる便利な1つの用語で、タイトルに含めるほど重要ではありません。

標準クラス宣言内のオプションのextends句は、現在のクラスの直接スーパークラスを指定します。

Superclass: ClassExtends:
extends ClassType

extends句は最初のクラスであり、直接スーパークラスを持たないため、クラスObjectの定義内に出現しないようにする必要があります。出現すると、コンパイル時にエラーが発生します。

ClassTypeは、アクセス可能なクラス型(6.6)に名前を付ける必要があります。そうでない場合、コンパイル時にエラーが発生します。

クラスfinalはサブクラスを持つことが許可されていないため、finalであるクラスにClassTypeが名前を付けた場合はコンパイル時にエラーが発生します(8.1.1.2)。

ClassTypeがクラスEnumまたはEnumの呼出し(これは、enumクラスによってのみ拡張可能)に名前を付けた場合は、コンパイル時にエラーが発生します(8.9)。

ClassTypeには型引数があります。これは、整形式のパラメータ化された型(4.5)を表し、いずれの型引数もワイルドカード型引数にすることはできません。ワイルドカード型引数にすると、コンパイル時にエラーが発生します。

(場合によっては汎用の)クラス宣言C<F1,...,Fn> (n 0, C Object)が与えられたときに、extends句が存在する場合、クラス型C<F1,...,Fn>直接スーパークラスは、Cの宣言のextends句で与えられた型です。そうでない場合はObjectです。

汎用クラス宣言C<F1,...,Fn> (n > 0)の場合、パラメータ化されたクラス型C<T1,...,Tn> (この場合Ti (1 i n)は型)の直接スーパークラスは、D<U1 θ,...,Uk θ> (この場合D<U1,...,Uk>C<F1,...,Fn>の直接スーパークラス、およびθ[F1:=T1,...,Fn:=Tn]の置換)です。

4.10.2で説明されています。このセクションは、型のスーパークラス型ではなく、クラスのスーパークラス型にのみ関係します。

宣言にextends句が欠落しているクラスの直接スーパークラス型は次のとおりです。

8で示されているように、このセクションは、すべてのクラスに関するものであるため、すべてのクラスの関係を定義することが重要です。

クラスの直接スーパークラスは、その直接スーパークラス型によって名前が付けられるクラスです。クラスは、直接スーパークラスの直接サブクラスであると言うことができます。直接スーパークラスは、その実装が現在のクラスの実装の導出元であるクラスです。

サブクラススーパークラス関係は、直接サブクラススーパークラス関係の推移閉包です。クラスAがクラスCサブクラススーパークラスであるのは、次のいずれかの条件が満たされる場合です。

クラスCがクラスAスーパークラスであると言えるのは、Aが常にCのサブクラスである場合です。

クラスは、その直接スーパークラスの直接サブクラスであり、その各スーパークラスのサブクラスであると言い表されます。

直接スーパークラスが、ここで定義されている基本的な関係である場合、その関係で推移閉包によってスーパークラスに到達する方が、その逆のサブクラスを介して迂回するよりも概念的に自然です。

例8.1.4-1.直接スーパークラスおよびサブクラス

class Point { int x, y; }
final class ColoredPoint extends Point { int color; }
class Colored3DPoint extends ColoredPoint { int z; }  // error

この場合、これらの関係は次のとおりです。

クラスColored3dPointの宣言では、finalクラスColoredPointを拡張しようとするため、コンパイル時にエラーが発生します。

例8.1.4-2.スーパークラスおよびサブクラス

class Point { int x, y; }
class ColoredPoint extends Point { int color; }
final class Colored3dPoint extends ColoredPoint { int z; }

この場合、これらの関係は次のとおりです。

クラスCTクラスまたはインタフェースA直接依存するのは、TACextendsまたはimplements句内で、スーパークラスまたはスーパーインタフェースとして、またはスーパークラスまたはスーパーインタフェース名の完全修飾形式の修飾子として指定されている場合です。

クラスC参照型TクラスまたはインタフェースA依存するのは、次のいずれかの条件が満たされる場合です。

クラスがそれ自体に依存する場合は、コンパイル時にエラーが発生します。

クラスがロードされるときに、循環的に宣言されたクラスが実行時に検出された場合、ClassCircularityErrorがスローされます(12.2.1)。

例8.1.4-3.それ自体に依存するクラス

class Point extends ColoredPoint { int x, y; }
class ColoredPoint extends Point { int color; }

このプログラムを実行すると、クラスPointがそれ自体に依存しているため、コンパイル時にエラーが発生します。

8.1.5 スーパーインタフェース

クラス宣言内のオプションのimplements句は、宣言されているクラスの直接スーパーインタフェーススーパーインタフェース型であるインタフェースの名前インタフェース型をリストします。

Superinterfaces: ClassImplements:
implements InterfaceTypeList
InterfaceTypeList:
InterfaceType {, InterfaceType}

InterfaceTypeは、アクセス可能なインタフェース型(6.6)に名前を付ける必要があります。そうでない場合、コンパイル時にエラーが発生します。

InterfaceTypeに型引数がある場合、これは、整形式のパラメータ化された型(4.5)を表し、いずれの型引数もワイルドカード型引数にすることはできません。ワイルドカード型引数にすると、コンパイル時にエラーが発生します。

単一のimplements句内に同じインタフェースが複数回直接スーパーインタフェースとして指定されるによって名前が付けられると、コンパイル時にエラーが発生します。このことは、インタフェースに別の方法で名前が付けられている場合でも当てはまります。

例8.1.5-1.不正なスーパーインタフェース

class Redundant implements java.lang.Cloneable, Cloneable {
    int x;
}

このプログラムの場合、名前java.lang.CloneableおよびCloneableが同じインタフェースを参照するため、コンパイル時にエラーが発生します。

(場合によっては汎用の)クラス宣言C<F1,...,Fn> (n 0, C Object)が与えられたときに、implements句が存在する場合、クラス型C<F1,...,Fn>直接スーパーインタフェースは、Cの宣言のimplements句で与えられた型です。

汎用クラス宣言C<F1,...,Fn> (n > 0)の場合、パラメータ化されたクラス型C<T1,...,Tn> (この場合Ti (1 i n)は型)の直接スーパーインタフェースは、すべての型I<U1 θ,...,Uk θ> (この場合I<U1,...,Uk>C<F1,...,Fn>の直接スーパーインタフェース、およびθ[F1:=T1,...,Fn:=Tn]の置換)です。

4.10.2で説明されています。このセクションは、型のスーパーインタフェース型ではなく、クラスのスーパーインタフェース型にのみ関係します。

宣言にimplements句が欠落しているクラスは直接スーパーインタフェース型を持ちませんが、1つの例外として、15.9.5に定義されているように、匿名クラスはスーパーインタフェース型を持つことができます。

クラスの直接スーパーインタフェース型の1つによってインタフェースに名前が付けられる場合、そのインタフェースはクラスの直接スーパーインタフェースです。

次のいずれかが当てはまる場合、インタフェースIはクラスCスーパーインタフェースです。

1つのクラスが複数の方法でスーパーインタフェースを持つことができます。

クラスは、その直接スーパーインタフェースを直接実装し、そのスーパーインタフェースすべてを実装すると言い表されます。また、クラスはその直接スーパーインタフェースの直接サブクラスであり、そのすべてのスーパーインタフェースのサブクラスである、と言い表すこともできます。

従来より、サブクラスはクラスとクラスの関係ですが、多くの場合、クラスとインタフェースの関係を説明する名詞を使用すると便利です。「実装クラス」は不自然である場合があります(たとえば、Aがクラスまたはインタフェースであるとして、「CAのサブクラスである」と「CAのサブクラスまたは実装クラスである」を比較した場合)。

1つのクラスが、同じ汎用インタフェース(9.1.2)の異なるパラメータ化であるか、汎用インタフェースおよびその同じ汎用インタフェースに名前を付けるRAW型のパラメータ化サブタイプであるか、そのようなスーパータイプ(4.10.2)を持つ2つのインタフェース型のサブタイプに同時になる直接スーパークラス型と直接スーパーインタフェース型または2つの直接スーパーインタフェース型を宣言することはできませんそのような競合が発生すると、このようになると、コンパイル時にエラーが発生します。

この要件は、型イレイジャ(4.6)による翻訳をサポートするために導入されています。

例8.1.5-2.スーパーインタフェース

interface Colorable {
    void setColor(int color);
    int getColor();
}
enum Finish { MATTE, GLOSSY }
interface Paintable extends Colorable {
    void setFinish(Finish finish);
    Finish getFinish();
}

class Point { int x, y; }
class ColoredPoint extends Point implements Colorable {
    int color;
    public void setColor(int color) { this.color = color; }
    public int getColor() { return color; }
}
class PaintedPoint extends ColoredPoint implements Paintable {
    Finish finish;
    public void setFinish(Finish finish) {
        this.finish = finish;
    }
    public Finish getFinish() { return finish; }
}

この場合、これらの関係は次のとおりです。

クラスPaintedPointはスーパーインタフェースとしてColorableを持ちますが、これは、このスーパーインタフェースがColoredPointのスーパーインタフェースであると同時にPaintableのスーパーインタフェースであるためです。

例8.1.5-3.不正なインタフェースの複数継承

interface I<T> {}
class B implements I<Integer> {}
class C extends B implements I<String> {}

クラスCは、I<Integer>とI<String>の両方のサブタイプになろうとしているため、コンパイル時のエラーの原因となります。

...

8.1.6 クラス本体およびメンバー宣言

クラス本体には、クラスのメンバーの宣言、つまり、フィールド(8.3)、メソッド(8.4)、クラス(8.5)およびインタフェース(8.5)が含まれる場合があります。

クラス本体に、クラスのインスタンス・イニシャライザ(8.6)、静的イニシャライザ(8.7)、およびコンストラクタの宣言(8.8)を含めることもできます。

ClassBody:
{ {ClassBodyDeclaration} }
ClassBodyDeclaration:
ClassMemberDeclaration
InstanceInitializer
StaticInitializer
ConstructorDeclaration
ClassMemberDeclaration:
FieldDeclaration
MethodDeclaration
ClassDeclaration
InterfaceDeclaration
;

クラスCで宣言または継承されるメンバーmの宣言のスコープおよびシャドウ化については、6.3および6.4に規定されています。

C自体がネストしたクラスである場合は、包含スコープにmと同じ種類(変数、メソッドまたは型)および名前の定義がある可能性があります。(このスコープは、ブロック、クラスまたはパッケージである場合があります。)そのようなすべての場合において、Cで宣言または継承されたメンバーmによって、同じ種類および名前の他の定義がシャドウ化されます(6.4.1)。

8.2 クラス・メンバー

クラスのメンバーには、次がすべて含まれます。

privateとして宣言されるクラスのメンバーは、そのクラスのサブクラスによって継承されません。

protectedまたはpublicとして宣言されるクラスのメンバーのみが、クラスが宣言されるもの以外のパッケージで宣言されるサブクラスによって継承されます。

コンストラクタ、静的イニシャライザおよびインスタンス・イニシャライザはメンバーではないため、継承されません。

メンバーの型というフレーズを使用して、次のことを示します。

クラスのフィールド、メソッドおよびメンバー・クラスおよびインタフェースは、異なるコンテキストで使用され、異なる参照手順(6.5)によって区別されるため、同じ名前を使用できます。ただし、スタイルの面からお薦めできません。

...

8.3 フィールド宣言

クラスの変数は、フィールド宣言によって導入されます。

...

8.4 メソッド宣言

8.4.8 継承、オーバーライドおよび非表示

クラスCは、その直接スーパークラスから、スーパークラスのすべての具象メソッドm (staticとインスタンスの両方) (次がすべて当てはまるもの)を継承します。

特定の型のメンバーとしてのメソッドのシグネチャに関して、サブシグネチャ・テストを実行することが重要です。メソッドのシグネチャは、型パラメータのインスタンス化に応じて異なります。

クラスCは、その直接スーパークラスおよび直接スーパーインタフェーススーパーインタフェース型のすべてのabstractと、デフォルト(9.4)のメソッドm (次がすべて当てはまるもの)を継承します。

クラスは、そのスーパーインタフェーススーパーインタフェース型からprivateまたはstaticメソッドを継承しません。

メソッドは、シグネチャごとにオーバーライドされるか、隠されます。たとえば、クラスが同じ名前の2つのpublicメソッドを宣言し(8.4.9)、サブクラスがその1つをオーバーライドする場合、サブクラスは引き続きもう一方のメソッドを継承します。

例8.4.8-1.継承

interface I1 {
    int foo();
}

interface I2 {
    int foo();
}

abstract class Test implements I1, I2 {} 

ここで、abstractクラスTestはインタフェースI1からabstractメソッドfooを継承し、さらにインタフェースI2からabstractメソッドfooを継承します。I1からのfooの継承を特定する場合の主な疑問は、I2のメソッドfooI1のメソッドfooを「I2から」オーバーライドする(9.4.1.1)かという点です。I1I2はお互いのサブインタフェースではないため、そうはなりません。したがって、クラスTestの視点からは、I1からのfooの継承は制約を受けません。I2からのfooの継承も同様です。8.4.8.4に示されているとおり、クラスTestは両方のfooメソッドを継承できます。明らかに、これはabstractとして宣言する必要がありますが、そのようにしない場合は、具象メソッドで両方のabstract fooメソッドをオーバーライドします。

継承される具象メソッドにより、abstractまたはデフォルトのメソッドの継承を阻止できます。(8.4.8.19.4.1.1に示されているとおり、具象メソッドは、abstractまたはデフォルトのメソッドを「Cから」オーバーライドします。)また、あるスーパータイプ・メソッドが別のスーパータイプ・メソッドを「すでに」オーバーライドしている場合、前者によって後者の継承を阻止できます。これはインタフェースのルール(9.4.1)と同じであり、複数のデフォルトのメソッドが継承されて、ある実装が明確に他の実装に取って代わるようになる競合が回避されます。

8.4.8.1 オーバーライド(インスタンス・メソッドを使用)

次のすべてのことが当てはまる場合に限り、クラスCで宣言または継承されたインスタンス・メソッドmCは、クラスAで宣言された別のメソッドmACからオーバーライドします。

mCが非abstractで、abstractメソッドmACからオーバーライドする場合、mCは、mAをCから実装すると言い表されます。

オーバーライドされるメソッドmAstaticメソッドの場合、コンパイル時にエラーが発生します。

この点に関しては、メソッドのオーバーライドはフィールド(8.3)の非表示とは異なります。インスタンス変数はstatic変数を隠すことができるためです。

次のすべてのことが当てはまる場合に限り、クラスCで宣言または継承されたインスタンス・メソッドmCは、インタフェースIで宣言された別のメソッドmICからオーバーライドします。

いずれかのメソッドの仮パラメータがRAW型を持ち、他のメソッドの対応するパラメータがパラメータ化された型を持つ場合、オーバーライドするメソッドのシグネチャは、オーバーライドされるメソッドのシグネチャとは異なる場合があります。これにより、既存のコードの移行に対応し、汎用を活用できます。

オーバーライドの概念には、その宣言クラスのなんらかのサブクラスから別のメソッドをオーバーライドするメソッドが含まれます。これは、2つの方法で考えられます。

オーバーライドされるメソッドには、キーワードsuperを含むメソッド呼出し式(15.12)を使用してアクセスできます。修飾名またはスーパークラス型へのキャストは、オーバーライドされるメソッドへのアクセスの試行では、効果的ではありません。

この点に関しては、メソッドのオーバーライドはフィールドの非表示とは異なります。

strictfp修飾子があるかどうかは、オーバーライドするメソッドと実装する抽象メソッドのルールにはまったく影響しません。たとえば、FP-strictではないメソッドはFP-strictメソッドをオーバーライドでき、FP-strictメソッドはFP-strictではないメソッドをオーバーライドできます。

...

8.4.8.2 非表示(クラス・メソッドを使用)

クラスCstaticメソッドmを宣言または継承する場合、mは、クラスまたはインタフェースAで宣言されたメソッドm' (次がすべて当てはまるもの)を隠すと言い表されます。

シグネチャを特定する際に使用する型についての説明は、型パラメータを使用できないstaticメソッドとは無関係ですが、staticメソッドと、スーパータイプのインスタンス・メソッドを一致させる方法に影響します。

staticメソッドによってインスタンス・メソッドが隠される場合、コンパイル時にエラーが発生します。

この点に関しては、メソッドの非表示はフィールド(8.3)の非表示とは異なります。static変数はインスタンス変数を隠すことができるためです。また、非表示はシャドウ化(6.4.1)や不明瞭化(6.4.2)とは異なります。

隠されるメソッドには、修飾名を使用するか、キーワードsuperまたはスーパークラス型へのキャストを含むメソッド呼出し式(15.12)を使用してアクセスできます。

この点に関しては、メソッドの非表示はフィールドの非表示と同様です。

例8.4.8.2-1.隠されたクラス・メソッドの呼出し

隠されたクラス(static)メソッドは、型がメソッドの宣言を実際に含むクラスの型である参照を使用して呼び出すことができます。この点に関しては、staticメソッドの非表示はインスタンス・メソッドのオーバーライドとは異なります。次に例を示します。

class Super {
    static String greeting() { return "Goodnight"; }
    String name() { return "Richard"; }
}
class Sub extends Super {
    static String greeting() { return "Hello"; }
    String name() { return "Dick"; }
}
class Test {
    public static void main(String[] args) {
        Super s = new Sub();
        System.out.println(s.greeting() + ", " + s.name());
    }
}

出力の生成:

Goodnight, Dick

これは、greetingの呼出しはsの型(つまり、Super)を使用して、呼び出すクラス・メソッドをコンパイル時に特定するのに対し、nameの呼出しはsのクラス(つまり、Sub)を使用して、呼び出すインスタンス・メソッドを実行時に特定するためです。

8.4.8.3 オーバーライドおよび非表示の要件

戻り型R1d1メソッド宣言が、戻り型R2の別のメソッドd2の宣言をオーバーライドするか隠す場合、d1d2に対して戻り型置換可能(8.4.5)である必要があります。そうでない場合、コンパイル時にエラーが発生します。

このルールにより、オーバーライド時にメソッドの戻り型を改良する共変の戻り型が可能になります。

R1R2のサブタイプではない場合、@SuppressWarnings (9.6.4.5)によって非表示にされていないかぎり、コンパイル時に未チェック警告が発生します。

別のメソッドをオーバーライドまたは非表示にするメソッド(インタフェースで定義されたabstractメソッドを実装するメソッドを含む)は、チェックされた例外をオーバーライド済または非表示のメソッドよりも多くスローするように宣言されない可能性があります。

この点に関しては、メソッドのオーバーライドはフィールド(8.3)の非表示とは異なります。フィールドは他の型のフィールドを隠すことができるためです。

より正確に言うために、Bはクラスまたはインタフェース、ABのスーパークラスまたはスーパーインタフェースで、Bのメソッド宣言m2Aのメソッド宣言m1をオーバーライドするか隠すとします。次に、

型宣言TクラスまたはインタフェースCがメンバー・メソッドm1を持ち、TCで宣言されたメソッドm2またはTCスーパータイプスーパークラスまたはスーパーインタフェースAが存在し、次のことがすべて当てはまる場合は、コンパイル時にエラーが発生します。

イレイジャを介して汎用が実装されるため、これらの制限が必要となります。前述のルールは、同じクラスで宣言された同じ名前のメソッドは異なるイレイジャを持つ必要があることを示しています。また、型宣言クラスまたはインタフェースは、同じ汎用インタフェースの2つの別個の呼出しパラメータ化を実装したり拡張することができないことも示しています。

オーバーライドするメソッドまたは隠すメソッドのアクセス修飾子は、次のように、少なくともオーバーライドされるメソッドまたは隠されるメソッドと同程度のアクセスを提供する必要があります。

これらの用語の技術的な意味においては、privateメソッドは、オーバーライドすることも、隠すこともできません。このことは、サブクラスはそのいずれかのスーパークラスのprivateメソッドと同じシグネチャを持つメソッドを宣言でき、そのようなメソッドの戻り型またはthrows句は、スーパークラスのprivateメソッドの対応する戻り型または句との関係を持つ必要はないことを意味します。

...

8.5 メンバー・クラスおよびインタフェース宣言

メンバー・クラスは、宣言が別のクラスまたはインタフェース宣言の本体(8.1.69.1.4)に直接包含されているクラスです。メンバー・クラスはenumクラス(8.9)である場合があります。

メンバー・インタフェースは、宣言が別のクラスまたはインタフェース宣言の本体(8.1.69.1.4)に直接包含されているインタフェースです。メンバー・インタフェースは注釈インタフェース(9.6)である場合があります。

クラス内のメンバー・クラスまたはインタフェース宣言のアクセス可能性は、そのアクセス修飾子によって指定され、アクセス修飾子がない場合は6.6によって指定されます。

クラス内のメンバー型宣言について同じキーワードが修飾子として複数回出現する場合、またはアクセス修飾子publicprotectedおよびprivate (6.6)のうちの複数がメンバー型宣言に含まれる場合、コンパイル時にエラーが発生します。

このことについては、すでに8.1.1および9.1.1に記載されています。

メンバー・クラスまたはインタフェースのスコープおよびシャドウ化については、6.3および6.4に規定されています。

特定の名前を持つメンバー・クラスまたはインタフェースをクラスで宣言した場合、そのクラスまたはインタフェースの宣言は、クラスのスーパークラスおよびスーパーインタフェース内の同じ名前を持つメンバー・クラスおよびインタフェースのアクセス可能なすべての宣言を隠すとされています。

この点に関しては、メンバー・クラスおよびインタフェースを隠すことは、フィールド(8.3)を隠すことと同様です。

クラスは、その直接スーパークラスおよび直接スーパーインタフェースから、スーパークラスおよびスーパーインタフェースの非privateメンバー・クラスおよびインタフェースのうち、クラス内のコードからアクセス可能で、かつクラス内の宣言によって隠されていないものをすべて継承します。

クラスは、そのスーパークラスとスーパーインタフェースから、またはそのスーパーインタフェースのみから、同じ名前を持つ複数のメンバー・クラスまたはインタフェースを継承することがあります。そのような状況自体によってコンパイル時にエラーが発生することはありません。ただし、クラスの本体内でそのようなメンバー・クラスまたはインタフェースをその単純名で参照しようとする場合、参照があいまいであるため、コンパイル時にエラーが発生します。

複数のパスによって、同じメンバー・クラスまたはインタフェース宣言が1つのインタフェースから継承されることがあります。そのような状況では、そのメンバー・クラスまたはインタフェースは1回のみ継承されるとみなされ、あいまいになることなく、その単純名で参照できます。

8.5.1 staticメンバー・クラスおよびインタフェース宣言

staticキーワードは、非内部クラスまたはインタフェースTの本体内のメンバー・クラスCの宣言を変更する場合があります。この結果、Cが内部クラスではないことを宣言します。Tstaticメソッドの本体内にTの現在のインスタンスがないように、CにもTの現在のインスタンスはなく、字句的に囲むインスタンスもありません。

staticクラスに包含クラスの非staticメンバーの使用が含まれる場合、コンパイル時にエラーが発生します。

メンバー・インタフェースは暗黙的にstaticです(9.1.1)。メンバー・インタフェースの宣言でstatic修飾子の重複指定が許可されています。

8.8 コンストラクタ宣言

8.8.7 コンストラクタ本体

8.8.7.1 明示的なコンストラクタ呼出し
ExplicitConstructorInvocation:
[TypeArguments] this ( [ArgumentList] ) ;
[TypeArguments] super ( [ArgumentList] ) ;
ExpressionName . [TypeArguments] super ( [ArgumentList] ) ;
Primary . [TypeArguments] super ( [ArgumentList] ) ;

便宜上、ここでは4.5.1および15.12の次のプロダクションを示します。

TypeArguments:
< TypeArgumentList >
ArgumentList:
Expression {, Expression}

明示的なコンストラクタ呼出し文は、次の2種類に分類されます。

コンストラクタ本体内の明示的なコンストラクタ呼出し文では、このクラスやスーパークラスで宣言されたインスタンス変数、インスタンス・メソッドまたは内部クラスを参照したり、式でthisまたはsuperを使用することはできません。そのようにすると、コンパイル時にエラーが発生します。

このように現在のインスタンスの使用が禁止されていることが、明示的なコンストラクタ呼出し文が静的コンテキストに出現する(8.1.3)とみなされる理由となっています。

TypeArgumentsthisまたはsuperの左側にある場合、いずれかの型引数がワイルドカードであれば(4.5.1)、コンパイル時にエラーが発生します。

Cはインスタンス化されるクラスであり、SCの直接スーパークラスであるとします。

スーパークラス・コンストラクタ呼出し文が修飾されていない場合、次のようになります。

スーパークラス・コンストラクタ呼出し文が修飾されている場合、次のようになります。

明示的なコンストラクタ呼出し文がスローできる例外型については、11.2.2に規定されています。

代替コンストラクタ呼出し文の評価では、まず、通常のメソッド呼出しと同様に、コンストラクタの引数が左から右に評価され、続いてコンストラクタが呼び出されます。

スーパークラス・コンストラクタ呼出し文の評価は、次のように進められます。

  1. iは、作成されるインスタンスであるとします。S (ある場合)に関してiを直接包含するインスタンスを特定する必要があります。

    • Sが内部クラスでない場合、またはSの宣言が静的コンテキストに出現する場合、Sに関してiを直接包含するインスタンスは存在しません。

    • スーパークラス・コンストラクタ呼出しが修飾されていない場合、Sは必然的にローカル・クラスまたは内部メンバー・クラスです。

      Sがローカル・クラスである場合、Oは、Sを直接包含するクラスまたはインタフェース宣言であるとします。

      Sが内部メンバー・クラスである場合、Oは、SがメンバーであるCの最も内側の包含クラスであるとします。

      nは、OCn番目の字句的な包含クラスまたはインタフェース宣言であるような整数であるとします(n 1)。

      Sに関してiを直接包含するインスタンスは、thisn番目の字句的な包含インスタンスです。

      継承によってSCのメンバーである場合もありますが、thisのゼロ番目の字句的な包含インスタンス(つまり、this自体)が、Sに関してiを直接包含するインスタンスとして使用されることはありません。

    • スーパークラス・コンストラクタ呼出しが修飾されている場合、.superの直前のPrimary式またはExpressionNamepが評価されます。

      pnullと評価されると、NullPointerExceptionが発生し、スーパークラス・コンストラクタ呼出しは突然完了します。

      それ以外の場合は、この評価の結果が、Sに関してiを直接包含するインスタンスです。

  2. S (ある場合)に関してiを直接包含するインスタンスが特定されたら、スーパークラス・コンストラクタ呼出し文の評価では、通常のメソッド呼出しと同様に、コンストラクタの引数が左から右に評価され、続いてコンストラクタが呼び出されます。

  3. 最後に、スーパークラス・コンストラクタ呼出し文が正常に完了すると、Cのすべてのインスタンス変数イニシャライザとCのすべてのインスタンス・イニシャライザが実行されます。インスタンス・イニシャライザまたはインスタンス変数イニシャライザIのテキストが別のインスタンス・イニシャライザまたはインスタンス変数イニシャライザJの前にある場合、IJより先に実行されます。

    スーパークラス・コンストラクタ呼出しが実際に明示的なコンストラクタ呼出し文として出現するか、暗黙的に提供されるかに関係なく、インスタンス変数イニシャライザおよびインスタンス・イニシャライザの実行は実施されます。(代替コンストラクタ呼出しでは、こうした追加の暗黙的な実行は実施されません。)

例8.8.7.1-1.明示的なコンストラクタ呼出し文に関する制限

8.8.7の例にあるColoredPointという最初のコンストラクタが次のように変更されたとします。

class Point {
    int x, y;
    Point(int x, int y) { this.x = x; this.y = y; }
}
class ColoredPoint extends Point {
    static final int WHITE = 0, BLACK = 1;
    int color;
    ColoredPoint(int x, int y) {
        this(x, y, color);  // Changed to color from WHITE
    }
    ColoredPoint(int x, int y, int color) {
        super(x, y);
        this.color = color;
    }
}

この場合、インスタンス変数colorを明示的なコンストラクタ呼出し文で使用することはできないため、コンパイル時にエラーが発生します。

例8.8.7.1-2.修飾されたスーパークラス・コンストラクタ呼出し

次のコードで、ChildOfInnerには字句的な包含クラスまたはインタフェース宣言がないため、ChildOfInnerのインスタンスには包含インスタンスがありません。ただし、ChildOfInnerのスーパークラス(Inner)には字句的な包含クラス宣言(Outer)があるため、InnerのインスタンスにはOuterの包含インスタンスが必要です。Outerの包含インスタンスは、Innerのインスタンスの作成時に設定されます。したがって、暗黙的にInnerのインスタンスであるChildOfInnerのインスタンスを作成する際に、ChildOfInnerのコンストラクタ内で修飾されたスーパークラス呼出し文を使用してOuterの包含インスタンスを提供する必要があります。Outerのインスタンスは、Innerに関してChildOfInnerを直接包含するインスタンスと呼ばれます。

class Outer {
    class Inner {}
}
class ChildOfInner extends Outer.Inner {
    ChildOfInner() { (new Outer()).super(); }
}

意外に感じられますが、Outerの同じインスタンスが、ChildOfInnerの複数のインスタンスについてInnerに関してChildOfInnerを直接包含するインスタンスとして機能することもあります。ChildOfInnerのこれらのインスタンスは、Outerの同じインスタンスに暗黙的にリンクされます。次のプログラムでは、Outerのインスタンスを、修飾されたスーパークラス・コンストラクタ呼出し文でそのインスタンスを使用するChildOfInnerのコンストラクタに渡すことによって、このことを実現します。明示的なコンストラクタ呼出し文のルールでは、その文を含む、コンストラクタの仮パラメータを使用することは禁止されていません。

class Outer {
    int secret = 5;
    class Inner {
        int  getSecret()      { return secret; }
        void setSecret(int s) { secret = s; }
    }
}
class ChildOfInner extends Outer.Inner {
    ChildOfInner(Outer x) { x.super(); }
}

public class Test {
    public static void main(String[] args) {
        Outer x = new Outer();
        ChildOfInner a = new ChildOfInner(x);
        ChildOfInner b = new ChildOfInner(x);
        System.out.println(b.getSecret());
        a.setSecret(6);
        System.out.println(b.getSecret());
    }
}

このプログラムでは、次の出力が生成されます。

5
6

その結果、ChildOfInnerの異なるインスタンスへの参照が従来の意味で別名でなくても、そのような参照を通じて、Outerの共通インスタンス内のインスタンス変数の操作を見ることができます。

8.9 enumクラス

enum宣言では、名前付きクラス・インスタンスの小規模なセットを定義する特別な種類のクラスである、新しいenumクラスを指定します。

EnumDeclaration:
{ClassModifier} enum TypeIdentifier [Superinterfaces ClassImplements] EnumBody

enum宣言では、最上位enumクラス(7.6)またはメンバーenumクラス(8.59.5)を指定できます。

enum宣言に修飾子abstractまたはfinalが含まれている場合、コンパイル時にエラーが発生します。

enum宣言は、クラス本体を持つenum定数(8.9.1)を1つ以上含んでいる場合を除き、暗黙的にfinalです。

ネストしたメンバーenumクラスは、暗黙的にstaticです。ネストしたメンバーenumクラスの宣言ではstatic修飾子を重複して指定することが許可されています。

これは、内部クラス(8.1.3)は、定数変数を除いてstaticメンバーを持つことができないため、内部クラスの本体でのメンバーとしてenumクラスを宣言できないことを意味します。

enum宣言について同じキーワードが修飾子として複数回出現する場合、またはアクセス修飾子publicprotectedおよびprivate (6.6)のうちの複数がenum宣言に含まれる場合、コンパイル時にエラーが発生します。

enum宣言はextends句を持ちません。enumクラスEの直接スーパークラスは、Enum<E>です(8.1.4)。

enumクラスには、そのenum定数によって定義されたもの以外のインスタンスはありません。enumクラスを明示的にインスタンス化しようとすると(15.9.1)、コンパイル時にエラーが発生します。

enumクラスのインスタンスが、そのenum定数によって定義されたもの以外に存在することがないようにするために、コンパイル時エラーに加えて、さらに次の3つのメカニズムが用意されています。

8.9.1 enum定数

enum宣言の本体には、enum定数を含めることができます。enum定数では、enumクラスのインスタンスを定義します。

EnumBody:
{ [EnumConstantList] [,] [EnumBodyDeclarations] }
EnumConstantList:
EnumConstant {, EnumConstant}
EnumConstant:
{EnumConstantModifier} Identifier [( [ArgumentList] )] [ClassBody]
EnumConstantModifier:
Annotation

便宜上、ここでは15.12の次のプロダクションを示します。

ArgumentList:
Expression {, Expression}

enum定数宣言の注釈修飾子に関するルールについては、9.7.4および9.7.5に規定されています。

EnumConstantIdentifierは、enum定数を参照するために名前で使用できます。

enum定数のスコープおよびシャドウ化については、6.3および6.4に規定されています。

enum定数の後に、引数(このセクションで後述するように、クラスの初期化中に定数が作成されるときにenumのコンストラクタに渡される)を指定できます。呼び出されるコンストラクタは、オーバーロード解決(15.12.2)の通常のルールを使用して選択されます。引数を省略すると、引数リストは空であるとみなされます。

enum定数のオプションのクラス本体では、直接包含するenumクラスを拡張する匿名クラス宣言(15.9.5)を暗黙的に定義します。クラス本体は、無名クラスの通常のルールに準拠します。たとえば、コンストラクタを含めることはできません。これらのクラス本体で宣言されたインスタンス・メソッドを包含enumクラスの外部で呼び出すことができるのは、包含enumクラス内のアクセス可能なメソッドをそれらがオーバーライドする場合のみです(8.4.8)。

enum定数のクラス本体でabstractメソッドを宣言した場合、コンパイル時にエラーが発生します。

冗長ですが、匿名クラスは非abstractになるように定義されます。

それぞれのenum定数のインスタンスは1つのみであるため、2つのオブジェクト参照を比較するときに、それらの少なくとも一方がenum定数を参照することがわかっていれば、equalsメソッドのかわりに==演算子を使用することが許可されています。

Enum内のequalsメソッドは、単にその引数でsuper.equalsを呼び出してその結果を返すことにより、アイデンティティ比較を実行するfinalメソッドです。

8.9.2 enum本体宣言

enum定数に加えて、enum宣言の本体には、コンストラクタとメンバーの宣言、インスタンス・イニシャライザおよび静的イニシャライザを含めることができます。

EnumBodyDeclarations:
; {ClassBodyDeclaration}

便宜上、ここでは8.1.6の次のプロダクションを示します。

ClassBodyDeclaration:
ClassMemberDeclaration
InstanceInitializer
StaticInitializer
ConstructorDeclaration
ClassMemberDeclaration:
FieldDeclaration
MethodDeclaration
ClassDeclaration
InterfaceDeclaration
;

enum宣言の本体内のコンストラクタ宣言またはメンバー宣言は、他に明示的に示されている場合を除き、それらが標準クラス宣言の本体内にある場合とまったく同様にenumクラスに適用されます。

enum宣言内のコンストラクタ宣言がpublicまたはprotected (6.6)である場合、コンパイル時にエラーが発生します。

enum宣言内のコンストラクタ宣言にスーパークラス・コンストラクタ呼出し文(8.8.7.1)が含まれている場合、コンパイル時にエラーが発生します。

enum宣言のコンストラクタ、インスタンス・イニシャライザまたはインスタンス変数イニシャライザからenumクラスstaticフィールドを参照すると、コンパイル時にエラーが発生します。ただし、フィールドが定数変数(4.12.4)である場合は除きます。

enum宣言で、アクセス修飾子がないコンストラクタ宣言はprivateです。

コンストラクタ宣言がないenum宣言では、デフォルト・コンストラクタが暗黙的に宣言されます。デフォルト・コンストラクタはprivateであり、仮パラメータもthrows句も持ちません。

実際には、コンパイラは、enumクラスのデフォルト・コンストラクタでStringおよびintパラメータを宣言することによって、Enumクラスをミラーリングする可能性があります。ただし、個々のコンパイラがデフォルト・コンストラクタの形式について合意する必要はないため、これらのパラメータは「暗黙的に宣言される」ものとして規定されていません。enum宣言のコンパイラのみが、enum定数をインスタンス化する方法を認識しています。他のコンパイラは、enumクラスの暗黙的に宣言されたpublic staticフィールド(8.9.3)がどのように初期化されたかを考慮することなく、単にそれらのフィールドを利用できます。

enum宣言Eabstractメソッドmがメンバーとして含まれている場合、コンパイル時にエラーが発生します。ただし、Eにenum定数が少なくとも1つあり、かつ、mの具体的な実装を提供するクラス本体がEのenum定数すべてにある場合は除きます。

enum宣言でファイナライザ(12.6)を宣言すると、コンパイル時にエラーが発生します。enumクラスのインスタンスをファイナライズすることはできません。

例8.9.2-1.enum本体宣言

enum Coin {
    PENNY(1), NICKEL(5), DIME(10), QUARTER(25);
    Coin(int value) { this.value = value; }

    private final int value;
    public int value() { return value; }
}

それぞれのenum定数は、コンストラクタを介して渡される、フィールドvalueの異なる値を準備します。このフィールドは、アメリカの硬貨の値をセントで表します。enum型のクラスのコンストラクタで宣言できるパラメータに制限はありません。

例8.9.2-2.enum定数の自己参照に関する制限

staticフィールド・アクセスに関するルールがないと、enumクラスに固有の初期化の循環性が原因で、一見妥当なコードであっても実行時に失敗します。(循環性は、自己型付けされるstaticフィールドがあるクラスに存在します。)失敗するコードの例を次に示します。

import java.util.Map;
import java.util.HashMap;

enum Color {
    RED, GREEN, BLUE;
    Color() { colorMap.put(toString(), this); }

    static final Map<String,Color> colorMap =
        new HashMap<String,Color>();
}

enum定数のコンストラクタが実行されるときにstatic変数colorMapが初期化されていないため、このenumの静的初期化はNullPointerExceptionをスローします。前述の制限によって、このようなコードをコンパイルすることはできません。ただし、正しく機能するように、このコードを次のように簡単にリファクタできます。

import java.util.Map;
import java.util.HashMap;

enum Color {
    RED, GREEN, BLUE;

    static final Map<String,Color> colorMap =
        new HashMap<String,Color>();
    static {
        for (Color c : Color.values())
            colorMap.put(c.toString(), c);
    }
}

静的初期化が上から下に発生するため、リファクタされたバージョンは明らかに適切です。

8.9.3 enumメンバー

enumクラスEのメンバーは、次のすべてです。

つまり、enumクラスEの宣言には、Eのenum定数に対応する暗黙的に宣言されたフィールドと競合するフィールドを含めることも、暗黙的に宣言されたメソッドと競合したり、クラスEnum<E>finalメソッドをオーバーライドするメソッドを含めることもできないということになります。

例8.9.3-1.拡張されたforループを使用したenum定数の反復

public class Test {
    enum Season { WINTER, SPRING, SUMMER, FALL }

    public static void main(String[] args) {
        for (Season s : Season.values())
            System.out.println(s);
    }
}

このプログラムでは、次の出力が生成されます。

WINTER
SPRING
SUMMER
FALL

例8.9.3-2.enum定数の切替え

switch文(14.11)は、enumクラスの外部からクラスへのメソッドの追加をシミュレートする場合に便利です。この例では、8.9.2Coinクラスcolorメソッドを追加し、硬貨、それらの値およびそれらの色の表を出力します。

class Test {
    enum CoinColor { COPPER, NICKEL, SILVER }

    static CoinColor color(Coin c) {
        switch (c) {
            case PENNY:
                return CoinColor.COPPER;
            case NICKEL:
                return CoinColor.NICKEL;
            case DIME: case QUARTER:
                return CoinColor.SILVER;
            default:
                throw new AssertionError("Unknown coin: " + c);
        }
    }

    public static void main(String[] args) {
        for (Coin c : Coin.values())
            System.out.println(c + "\t\t" +
                               c.value() + "\t" + color(c));
    }
}

このプログラムでは、次の出力が生成されます。

PENNY           1       COPPER
NICKEL          5       NICKEL
DIME            10      SILVER
QUARTER         25      SILVER

例8.9.3-3.クラス本体を含むenum定数

enum Operation {
    PLUS {
        double eval(double x, double y) { return x + y; }
    },
    MINUS {
        double eval(double x, double y) { return x - y; }
    },
    TIMES {
        double eval(double x, double y) { return x * y; }
    },
    DIVIDED_BY {
        double eval(double x, double y) { return x / y; }
    };

    // Each constant supports an arithmetic operation
    abstract double eval(double x, double y);

    public static void main(String args[]) {
        double x = Double.parseDouble(args[0]);
        double y = Double.parseDouble(args[1]);
        for (Operation op : Operation.values())
            System.out.println(x + " " + op + " " + y +
                               " = " + op.eval(x, y));
    }
}

クラス本体は、enum定数に動作を追加します。このプログラムでは次の出力が生成されます。

java Operation 2.0 4.0
2.0 PLUS 4.0 = 6.0
2.0 MINUS 4.0 = -2.0
2.0 TIMES 4.0 = 8.0
2.0 DIVIDED_BY 4.0 = 0.5

このパターンでは、新しい定数の動作を追加するのを忘れる可能性がなくなるので(enum宣言が原因でコンパイル時にエラーが発生するため)、基底クラス(Operation)でswitch文を使用するよりも、このパターンの方がはるかに安全です。

例8.9.3-4.複数のenumクラス

次のプログラムでは、2つの単純なenumに基づいてトランプのクラスが作成されています。

import java.util.List;
import java.util.ArrayList;
class Card implements Comparable<Card>,
                      java.io.Serializable {
    public enum Rank { DEUCE, THREE, FOUR, FIVE, SIX, SEVEN,
                       EIGHT, NINE, TEN,JACK, QUEEN, KING, ACE }

    public enum Suit { CLUBS, DIAMONDS, HEARTS, SPADES }

    private final Rank rank;
    private final Suit suit;
    public Rank rank() { return rank; }
    public Suit suit() { return suit; }

    private Card(Rank rank, Suit suit) {
        if (rank == null || suit == null)
            throw new NullPointerException(rank + ", " + suit);
        this.rank = rank;
        this.suit = suit;
    }

    public String toString() { return rank + " of " + suit; }

    // Primary sort on suit, secondary sort on rank
    public int compareTo(Card c) {
        int suitCompare = suit.compareTo(c.suit);
        return (suitCompare != 0 ?
                    suitCompare :
                    rank.compareTo(c.rank));
    }

    private static final List<Card> prototypeDeck =
        new ArrayList<Card>(52);

    static {
        for (Suit suit : Suit.values())
            for (Rank rank : Rank.values())
                prototypeDeck.add(new Card(rank, suit));
    }

    // Returns a new deck
    public static List<Card> newDeck() {
        return new ArrayList<Card>(prototypeDeck);
    }
}

次のプログラムでは、Cardクラスが実行されます。これは、カードを配るプレーヤの数とそれぞれのプレーヤのカードの数を表す2つの整数パラメータをコマンドラインで取得します。

import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
class Deal {
    public static void main(String args[]) {
        int numHands     = Integer.parseInt(args[0]);
        int cardsPerHand = Integer.parseInt(args[1]);
        List<Card> deck  = Card.newDeck();
        Collections.shuffle(deck);
        for (int i=0; i < numHands; i++)
            System.out.println(dealHand(deck, cardsPerHand));
    }

    /**
     * Returns a new ArrayList consisting of the last n
     * elements of deck, which are removed from deck.
     * The returned list is sorted using the elements'
     * natural ordering.
     */
    public static <E extends Comparable<E>>
    ArrayList<E> dealHand(List<E> deck, int n) {
        int deckSize = deck.size();
        List<E> handView = deck.subList(deckSize - n, deckSize);
        ArrayList<E> hand = new ArrayList<E>(handView);
        handView.clear();
        Collections.sort(hand);
        return hand;
    }
}

このプログラムでは次の出力が生成されます。

java Deal 4 3
[DEUCE of CLUBS, SEVEN of CLUBS, QUEEN of DIAMONDS]
[NINE of HEARTS, FIVE of SPADES, ACE of SPADES]
[THREE of HEARTS, SIX of HEARTS, TEN of SPADES]
[TEN of CLUBS, NINE of DIAMONDS, THREE of SPADES]

第9章 インタフェース

インタフェース宣言は、メンバーがクラス、インタフェース、定数およびメソッドである1つ以上のクラスによって実装できる新しい参照型インタフェースを導入します。プログラムでは、インタフェースを使用して、本来なら無関係なクラスに共通のスーパー・タイプを提供できます。

この型はインタフェースはインスタンス変数を持たず、通常は、1つ以上のabstractメソッドを宣言します宣言します。本来なら無関係なクラスにインタフェースを実装するには、そのabstractメソッドの実装を提供します。インタフェースを直接インスタンス化することはできません。

ネストしたインタフェースは、宣言が別のクラスまたはインタフェースの本体内に出現するインタフェースです。

最上位インタフェースは、ネストしたインタフェースではないインタフェースです。

最上位インタフェース(7.6)は、コンパイル・ユニットの最上位で宣言されるインタフェースです。

ネストしたインタフェースは、宣言が別のクラスまたはインタフェースのメンバー・インタフェース(8.59.5)として出現するインタフェースです。

標準インタフェースと注釈型という2種類のインタフェースを区別します。

注釈インタフェース(9.6)は、注釈(9.7)の反射型の表現によって実装することを目的とした、特別な構文で宣言されるインタフェースです。

この章では、すべてのインタフェース、つまり、標準インタフェース(最上位インタフェース(7.6)とネストしたインタフェース(8.59.5)の両方)および注釈型(9.6)の共通セマンティクスについて説明します。特定の種類のインタフェースに固有の詳細は、これらのコンストラクトに特化したセクションで説明します。

プログラムでは、インタフェースを使用して、関連するクラスが共通のabstractスーパークラスを共有したり、メソッドをObjectに追加しなくても済むようにすることができます。

インタフェースは、1つ以上の他のインタフェースの直接拡張として宣言できます。これは、それがオーバーライドしたり、隠す可能性があるメンバーを除き、それが拡張するインタフェースのすべてのメンバー・クラスおよびインタフェース、インスタンス・メソッドおよび定数staticフィールドを継承することを意味します。

クラスは、1つ以上のインタフェース(8.1.5)直接実装するように宣言できます。これは、インタフェースによって指定されたすべてのabstractメソッドをそのクラスのインスタンスが実装することを意味します。クラスは必然的に、その直接スーパークラスおよび直接スーパーインタフェースが実装するすべてのインタフェースを実装します。こうした(複数の)インタフェース継承によって、オブジェクトは、スーパークラスを共有することなく、(複数の)共通動作をサポートできます。

宣言された型がインタフェース型である変数は、指定されたインタフェースを実装するクラスのインスタンスへの参照をその値として持つことができます。インタフェースのすべてのabstractメソッドがクラスに実装されていても十分ではありません。クラス、またはそのスーパークラスのいずれかが、実際に、インタフェースを実装するように宣言されている必要があります。そうでない場合、クラスはインタフェースを実装するとみなされません。

9.1 インタフェース宣言

インタフェース宣言では、新しい名前付き参照型インタフェースを指定します。インタフェース宣言には、標準インタフェース宣言注釈インタフェース宣言(9.6)の2種類があります。

InterfaceDeclaration:
NormalInterfaceDeclaration
AnnotationTypeDeclaration
NormalInterfaceDeclaration:
{InterfaceModifier} interface TypeIdentifier [TypeParameters]
[ExtendsInterfaces InterfaceExtends] InterfaceBody

用語ExtendsInterfacesは、プロダクションがインタフェース型ではなく、インタフェースのリストであると誤って提案しています。用語InterfaceExtendsは、8.1ClassExtendsに合せたものです。

インタフェース宣言のTypeIdentifierでは、インタフェースの名前を指定します。

インタフェースの単純名がその包含クラスまたはインタフェースのいずれかと同じ場合、コンパイル時にエラーが発生します。

インタフェース宣言のスコープおよびシャドウ化については、6.3および6.4に規定されています。

9.1.1 インタフェース修飾子

インタフェース宣言にはインタフェース修飾子が含まれることがあります。

InterfaceModifier:
(次のうちの1つ)
Annotation public protected private
abstract static strictfp

インタフェース宣言での注釈修飾子のルールは、9.7.4および9.7.5に規定されています。

アクセス修飾子public (6.6)は、あらゆる種類のインタフェース宣言に関連しています。

abstractおよびstrictfpも同様です。なぜこの1つが挙げられているのでしょうか。

アクセス修飾子protectedおよびprivateおよびstaticは、宣言がクラス宣言によって直接包含される(8.5.1)メンバー・インタフェース(8.59.5)にのみ関連します。

修飾子staticは、メンバー・クラス(8.5.1, 9.5)にのみ関連し、最上位インタフェース(7.6)には関連しません。

8.1.1と変更内容を比較してください。

同一のキーワードがあるインタフェース宣言の1つの修飾子として複数回出現する、またはインタフェース宣言にアクセス修飾子publicprotected、およびprivateのいずれかが複数出現する場合は、コンパイル時にエラーが発生します(6.6)。

複数の(別個の)インタフェース修飾子が1つのインタフェース宣言に出現すると、InterfaceModifierのプロダクションでは、必須ではありませんが、慣行として前述に従った順序で表示されます。

9.1.3 スーパーインタフェースおよびサブインタフェース

ここで説明するコア機能は、インタフェースのスーパーインタフェースのセットです。「サブインタフェース」は単なる便利な1つの用語で、タイトルに含めるほど重要ではありません。

extends句が指定される場合、宣言されているインタフェースが、それぞれの他の名前付きインタフェースリストされているインタフェース型を拡張するため、それぞれの他の名前付きインタフェースリストされているインタフェース型のメンバー・クラスおよびインタフェース、インスタンス・メソッドおよび定数を継承します。

これらの他の名前付きインタフェースリストされているインタフェース型は、宣言されているインタフェースの直接スーパーインタフェーススーパーインタフェース型です。

宣言されたインタフェースをimplementsするクラスは、このインタフェースがextendsするすべてのインタフェースも実装するとみなされます。

このことは8.1.5で説明されています。ここでは、「実装するとみなされます」はあいまいな言い方です。

ExtendsInterfaces: InterfaceExtends:
extends InterfaceTypeList

便宜上、ここでは8.1.5の次のプロダクションを示します。

InterfaceTypeList:
InterfaceType {, InterfaceType}

インタフェース宣言のextends句内の各InterfaceTypeは、アクセス可能なインタフェース型(6.6)に名前を付ける必要があります。そうでない場合、コンパイル時にエラーが発生します。

InterfaceTypeに型引数がある場合、これは、整形式のパラメータ化された型(4.5)を表し、いずれの型引数もワイルドカード型引数にすることはできません。ワイルドカード型引数にすると、コンパイル時にエラーが発生します。

(場合によっては汎用の)インタフェース宣言I<F1,...,Fn> (n 0)が与えられたときに、extends句が存在する場合、インタフェース型I<F1,...,Fn>直接スーパーインタフェースは、Iの宣言のextends句で与えられた型です。

汎用インタフェース宣言I<F1,...,Fn> (n > 0)の場合、パラメータ化されたインタフェース型I<T1,...,Tn> (この場合Ti (1 i n)は型)の直接スーパーインタフェースは、すべての型J<U1 θ,...,Uk θ> (この場合J<U1,...,Uk>I<F1,...,Fn>の直接スーパーインタフェース、およびθ[F1:=T1,...,Fn:=Tn]の置換)です。

4.10.2で説明されています。このセクションは、型のスーパーインタフェース型ではなく、インタフェースのスーパーインタフェース型にのみ関係します。

注釈型の直接スーパーインタフェース型は、暗黙的にjava.lang.annotation.Annotationです。

あるインタフェースが別のインタフェースの直接スーパーインタフェースになるのは、後者のインタフェースの直接スーパーインタフェース型の1つによって前者のインタフェースに名前が付けられる場合です。

スーパーインタフェース関係は、直接スーパーインタフェース関係の推移閉包です。次のいずれかが当てはまる場合、インタフェースKはインタフェースIのスーパーインタフェースです。

KIのスーパーインタフェースである場合は常に、インタフェースIKサブインタフェースであると言われます。インタフェースはその直接スーパーインタフェースの直接サブインタフェースであり、その各スーパーインタフェースのサブインタフェースであると言い表されます。

8.1.48.1.5に合せたこれらの定義の表象的な変更。

すべてのクラスがクラスObjectの拡張ですが、すべてのインタフェースが拡張である単一のインタフェースは存在しません。

インタフェースITクラスまたはインタフェースA直接依存するのは、TAIextends句内で、スーパーインタフェースとして、またはスーパーインタフェース名の完全修飾形式の修飾子として指定されている場合です。

インタフェースI参照型TクラスまたはインタフェースA依存するのは、次のいずれかの条件が満たされる場合です。

インタフェースがそれ自体に依存する場合、コンパイル時にエラーが発生します。

インタフェースがロードされるときに、循環的に宣言されたインタフェースが実行時に検出された場合、ClassCircularityErrorがスローされます(12.2.1)。

9.1.4 インタフェース本体およびメンバー宣言

インタフェースの本体は、インタフェースのメンバー、つまり、フィールド(9.3)、メソッド(9.4)、クラス(9.5)およびインタフェース(9.5)を宣言する場合があります。

InterfaceBody:
{ {InterfaceMemberDeclaration} }
InterfaceMemberDeclaration:
ConstantDeclaration
InterfaceMethodDeclaration
ClassDeclaration
InterfaceDeclaration
;

インタフェースIで宣言または継承されるメンバーmの宣言のスコープについては、6.3に規定されています。

9.2 インタフェース・メンバー

インタフェースのメンバーは、次のとおりです。

インタフェースは、それによって拡張されるインタフェースから、そのすべてのメンバーを継承します。ただし、(i)それによって隠されるフィールド、クラスおよびインタフェース、(ii)それによってオーバーライドされるabstractメソッドおよびデフォルトのメソッド(9.4.1)、(iii) privateメソッドおよび(iv) staticメソッドは除きます。

インタフェースのフィールド、メソッドおよびメンバー・クラスおよびインタフェースは、異なるコンテキストで使用され、異なる参照手順(6.5)によって区別されるため、同じ名前を使用できます。ただし、スタイルの面からお薦めできません。

9.4 メソッド宣言

9.4.1 継承およびオーバーライド

インタフェースIは、その直接スーパーインタフェーススーパーインタフェース型のすべてのabstractと、デフォルトのメソッドm (次がすべて当てはまるもの)を継承します。

メソッドは、シグネチャごとにオーバーライドされます。たとえば、インタフェースが同じ名前の2つのpublicメソッドを宣言し(9.4.2)、サブインタフェースがその1つをオーバーライドする場合、サブインタフェースは引き続きもう一方のメソッドを継承します。

前述の3つ目の句により、サブインタフェースが、そのスーパーインタフェースの別のサブインタフェースによってすでにオーバーライドされたメソッドを再度継承することが防止されます。たとえば、次のプログラムでは、

interface Top {
    default String name() { return "unnamed"; }
}
interface Left extends Top {
    default String name() { return getClass().getName(); }
}
interface Right extends Top {}

interface Bottom extends Left, Right {}

RightTopからname()を継承しますが、BottomRightではなくLeftからname()を継承します。これは、Leftからのname()Topname()の宣言をオーバーライドするためです。

インタフェースは、そのスーパーインタフェースからprivateまたはstaticメソッドを継承しません。

インタフェースIprivateまたはstaticメソッドmを宣言し、mのシグネチャがIのスーパーインタフェースpublicインスタンス・メソッドm'のサブシグネチャであり、かつそれ以外の方法でm'がIのコードにアクセス可能な場合は、コンパイル時にエラーが発生します。

本質的には、インタフェースのstaticメソッドは、スーパーインタフェースのインスタンス・メソッドを隠すことができません。これは、クラスのstaticメソッドが、スーパークラスまたはスーパーインタフェースのインスタンス・メソッドを隠すことができない、8.4.8.2のルールと同様です。インタフェースはstaticメソッドを継承できないため、前述のルールでは「staticメソッドを宣言する」インタフェースのみが規定されていますが、8.4.8.2のルールでは、「staticメソッドを宣言または継承する」クラスが規定されています。また、前述のルールではスーパーインタフェーススーパーインタフェース型publicインスタンス・メソッドのみが考慮されていますが、8.4.8.2のルールではスーパークラスまたはスーパーインタフェースのインスタンス・メソッドとstaticメソッドの両方を隠すことが許可されます。

同様に、インタフェースのprivateメソッドは、スーパーインタフェースのインスタンス・メソッド(publicprivateかは問わない)をオーバーライドできません。このことは8.4.8.1および8.4.8.3のルールと同様で、つまり、8.4.8.1によって、オーバーライドされるメソッドを非privateにすることが要求され、8.4.8.3によって、オーバーライドするメソッドが少なくともオーバーライドされるメソッドと同程度のアクセスを提供することが要求されるため、クラスのprivateメソッドは、スーパークラスまたはスーパーインタフェースのインスタンス・メソッドをオーバーライドできません。つまり、インタフェースのpublicメソッドのみを、サブインタフェースまたは実装クラスのpublicメソッドのみによってオーバーライドできます。

9.4.1.1 オーバーライド(インスタンス・メソッドを使用)

次のすべてのことが当てはまる場合に限り、インタフェースIで宣言または継承されたインスタンス・メソッドmIは、インタフェースJで宣言された別のインスタンス・メソッドmJIからオーバーライドします。

strictfp修飾子があるかどうかは、オーバーライドするメソッドのルールにはまったく影響しません。たとえば、FP-strictではないメソッドはFP-strictメソッドをオーバーライドでき、FP-strictメソッドはFP-strictではないメソッドをオーバーライドできます。

オーバーライドされるデフォルトのメソッドには、スーパーインタフェース名で修飾されたキーワードsuperを含むメソッド呼出し式(15.12)を使用してアクセスできます。

9.5 メンバー・クラスおよびインタフェース宣言

インタフェースには、メンバー・クラスおよびインタフェース宣言(8.5)を含めることができます。

インタフェースの本体内のそれぞれのメンバー・クラスまたはインタフェース宣言は、暗黙的にpublicかつstaticです。これらの修飾子のいずれかまたは両方を重複して指定することが許可されています。

インタフェースのメンバー・クラスまたはインタフェース宣言に修飾子protectedまたはprivateが含まれている場合、コンパイル時にエラーが発生します。

インタフェースのメンバー型宣言について同じキーワードが修飾子として複数回出現する場合、コンパイル時にエラーが発生します。

このことについては、すでに8.1.1および9.1.1に記載されています。

特定の名前を持つメンバー・クラスまたはインタフェースをインタフェースで宣言した場合、そのクラスまたはインタフェースの宣言は、インタフェースのスーパーインタフェース内の同じ名前を持つメンバー・クラスおよびインタフェースのアクセス可能なすべての宣言を隠すとされています。

インタフェースは、その直接スーパーインタフェースから、スーパーインタフェースのprivateメンバー・クラスおよびインタフェースのうち、インタフェース内のコードからアクセス可能で、かつインタフェース内の宣言によって隠されていないものをすべて継承します。

スーパーインタフェースのメンバー・クラスおよびインタフェースはすべてpublicであるため、それらのアクセス可能性をここで考慮する必要はありません。

インタフェースは、同じ名前を持つ複数のメンバー・クラスまたはインタフェースを継承することがあります。そのような状況自体によってコンパイル時にエラーが発生することはありません。ただし、インタフェースの本体内でそのようなメンバー・クラスまたはインタフェースをその単純名で参照しようとする場合、参照があいまいであるため、コンパイル時にエラーが発生します。

複数のパスによって、同じメンバー・クラスまたはインタフェース宣言が1つのインタフェースから継承されることがあります。そのような状況では、そのメンバー・クラスまたはインタフェースは1回のみ継承されるとみなされ、あいまいになることなく、その単純名で参照できます。

9.6 注釈インタフェース

注釈宣言では、特別な種類のインタフェースである、新しい注釈インタフェースを指定します。注釈宣言を標準インタフェース宣言と区別するために、キーワードinterfaceの前にアットマークアットマーク(@)が付けられます。

AnnotationTypeDeclaration: AnnotationDeclaration:
{InterfaceModifier} @ interface TypeIdentifier AnnotationTypeBody AnnotationInterfaceBody

アットマークアットマーク(@)とキーワードinterfaceは、別個のトークンです。それらを空白で区切ることもできますが、スタイルとしてはお薦めしません。

注釈宣言の注釈修飾子に関するルールについては、9.7.4および9.7.5に規定されています。

注釈宣言のTypeIdentifierでは、注釈インタフェースの名前を指定します。

注釈インタフェースの単純名がその包含クラスまたはインタフェースのいずれかと同じ場合、コンパイル時にエラーが発生します。

それぞれの注釈インタフェースの直接スーパーインタフェースjava.lang.annotation.Annotationです(9.1.3)

AnnotationTypeDeclaration AnnotationInterfaceDeclaration構文により、注釈インタフェース宣言を汎用にすることはできず、extends句も許可されていません。

注釈インタフェースではスーパークラスまたはスーパーインタフェースを明示的に宣言できないため、注釈インタフェースサブクラスまたはサブインタフェース自体が注釈インタフェースになることはありません。同様に、java.lang.annotation.Annotation自体も注釈インタフェースにはなりません。

注釈インタフェースは、様々なメンバーをjava.lang.annotation.Annotationから継承します。これには、Objectのインスタンス・メソッドに対応する暗黙的に宣言されたメソッドも含まれますが、これらのメソッドでは、注釈インタフェースの要素(9.6.1)は定義されません。

これらのメソッドでは注釈インタフェースの要素が定義されないため、その型の注釈(9.7)でそれらを使用することは不正です。このルールがなければ、要素が注釈で表現できる型であることや、それらのアクセサ・メソッドを使用できることを確認できません。

ここで明示的に変更されていないかぎり、標準インタフェース宣言に適用されるすべてのルールが注釈宣言にも適用されます。

たとえば、注釈インタフェースは、標準クラスおよびインタフェース型クラスおよびインタフェースと同じネームスペースを共有します。また、注釈宣言は、インタフェース宣言が正当である場合は常に正当であり、インタフェース宣言と同じスコープおよびアクセス可能性を持ちます。

9.6.1 注釈要素

注釈宣言の本体にはメソッド宣言を含めることができ、それぞれの宣言で、注釈インタフェース要素を定義します。注釈インタフェースは、それが明示的に宣言するメソッドで定義されたもの以外の要素を持ちません。

AnnotationTypeBody: AnnotationInterfaceBody:
{ { AnnotationTypeMemberDeclaration AnnotationMemberDeclaration } }
AnnotationTypeMemberDeclaration: AnnotationMemberDeclaration:
AnnotationTypeElementDeclaration AnnotationElementDeclaration
ConstantDeclaration
ClassDeclaration
InterfaceDeclaration
;
AnnotationTypeElementDeclaration: AnnotationElementDeclaration:
{ AnnotationTypeElementModifier AnnotationElementModifier } UnannType Identifier ( ) [Dims]
[DefaultValue] ;
AnnotationTypeElementModifier: AnnotationElementModifier:
(次のうちの1つ)
Annotation public
abstract

AnnotationTypeElementDeclaration AnnotationElementDeclarationプロダクションにより、注釈宣言内のメソッド宣言は、仮パラメータ、型パラメータまたはthrows区を持つことができません。便宜上、ここでは4.3の次のプロダクションを示します。

Dims:
{Annotation} [ ] {{Annotation} [ ]}

AnnotationTypeElementModifier AnnotationElementModifierプロダクションにより、注釈宣言内のメソッド宣言をdefaultまたはstaticにすることはできません。したがって、注釈インタフェースでは、標準インタフェースと同じ種類のメソッドを宣言できません。注釈インタフェースがその暗黙的なスーパーインタフェースjava.lang.annotation.Annotationからデフォルト・メソッドを継承することも考えられますが、Java SE 14の時点ではそのようなデフォルト・メソッドは存在しません。

規則により、注釈要素宣言に存在する必要がある唯一のAnnotationTypeElementModifier AnnotationElementModifierは注釈です。

注釈インタフェースで宣言されるメソッドの戻り型は、次のいずれかである必要があります。そうでない場合はコンパイル時にエラーが発生します。

このルールによって、次のようなネストした配列型を持つ要素を防ぐことができます。

@interface Verboten {
    String[][] value();
}

配列を返すメソッドの宣言では、空の仮パラメータ・リストの後ろに配列型を示すカッコのペアを配置することが許可されています。この構文は、以前のバージョンのJavaプログラミング言語との互換性のためにサポートされています。新しいコードではこの構文を使用しないことをお薦めします。

注釈インタフェースで宣言されたメソッドに、クラスObjectまたはインタフェースjava.lang.annotation.Annotationで宣言されたpublicまたはprotectedメソッドのものとオーバーライド等価であるシグネチャがある場合、コンパイル時にエラーが発生します。

注釈型宣言注釈インタフェースの宣言Tに型Tの要素が直接的または間接的に含まれている場合、コンパイル時にエラーが発生します。

たとえば、次は不正です。

@interface SelfRef { SelfRef value(); }

次も同様です。

@interface Ping { Pong value(); }
@interface Pong { Ping value(); }

要素がない注釈インタフェースは、マーカー注釈インタフェースと呼ばれます。

要素が1つの注釈インタフェースは、単一要素注釈インタフェースと呼ばれます。

規則により、単一要素注釈インタフェース内の唯一の要素の名前はvalueです。この規則の言語サポートは、単一要素注釈(9.7.3)によって提供されます。

例9.6.1-1.注釈宣言

次の注釈宣言では、要素が複数ある注釈インタフェースが定義されています。

/**
 * Describes the "request-for-enhancement" (RFE)
 * that led to the presence of the annotated API element.
 */
@interface RequestForEnhancement {
    int    id();        // Unique ID number associated with RFE
    String synopsis();  // Synopsis of RFE
    String engineer();  // Name of engineer who implemented RFE
    String date();      // Date RFE was implemented
}

例9.6.1-2.マーカー注釈宣言

次の注釈宣言では、マーカー注釈インタフェースが定義されています。

/**
 * An annotation with this type indicates that the 
 * specification of the annotated API element is 
 * preliminary and subject to change.
 */
@interface Preliminary {}

例9.6.1-3.単一要素注釈宣言

次の注釈宣言では、単一要素注釈インタフェースではvalueという要素が定義されるという規則を示しています。

/**
 * Associates a copyright notice with the annotated API element.
 */
@interface Copyright {
    String value();
}

次の注釈宣言では、唯一の要素が配列型を持つ単一要素注釈インタフェースが定義されています。

/**
 * Associates a list of endorsers with the annotated class.
 */
@interface Endorsers {
    String[] value();
}

次の注釈宣言は、制限付きのワイルドカードによって値が制約されているClass型の要素を示しています。

interface Formatter {}

// Designates a formatter to pretty-print the annotated class
@interface PrettyPrinter {
    Class<? extends Formatter> value();
}

次の注釈宣言には、型が同様に注釈インタフェース型である要素が含まれています。

/**
 * Indicates the author of the annotated program element.
 */
@interface Author {
    Name value();
}
/**
 * A person's name.  This annotation type is not designed
 * to be used directly to annotate program elements, but to
 * define elements of other annotation types.
 * A person's name.  This annotation interface is not designed
 * to be used directly to annotate program elements, but to
 * define elements of other annotation interfaces.
*/
@interface Name {
   String first();
   String last();
}

注釈宣言の文法では、メソッド要素宣言に加えて、他の要素メンバー宣言も許可されています。たとえば、注釈インタフェースとともに使用するネストしたenumを宣言することを選択できます。

@interface Quality {
    enum Level { BAD, INDIFFERENT, GOOD }
    Level value();
}

9.6.2 注釈要素のデフォルト

注釈要素宣言には、キーワードdefaultElementValueを含む要素の(空の)パラメータ・リストに従って指定されるデフォルト値を含めることができます(9.7.1)。

DefaultValue:
default ElementValue

要素の型が、指定されたデフォルト値に対応していない(9.7)場合、コンパイル時にエラーが発生します。

デフォルト値は注釈にコンパイルされず、注釈が読み取られるときに動的に適用されます。したがって、デフォルト値を変更すると、変更が加えられる前にコンパイルされたクラスの注釈にも影響します(これらの注釈に、デフォルトが指定された要素の明示的な値がないものと仮定します)。

例9.6.2-1.デフォルト値を含む注釈宣言

9.6.1RequestForEnhancement注釈インタフェースを改良したものを次に示します。

@interface RequestForEnhancementDefault {
    int    id();       // No default - must be specified in 
                       // each annotation
    String synopsis(); // No default - must be specified in 
                       // each annotation
    String engineer()  default "[unassigned]";
    String date()      default "[unimplemented]";
}

9.6.3 繰返し可能な注釈インタフェース

注釈TインタフェースAは、value要素がTAの包含注釈インタフェースを示す@Repeatable注釈(9.6.4.8)を使用してその宣言に(メタ)注釈が付けられている場合、繰返し可能です。

次のすべてに該当する場合、注釈TCインタフェースACは、TAの包含注釈インタフェースです。

  1. TCACによって、戻り型がT[]A[]であるvalue()メソッド要素が宣言されます。

  2. TCACによって宣言された、value()以外のメソッド要素にデフォルト値があります。

  3. 少なくともTAと同じ期間、TCACが保持されます(保持期間は、@Retention注釈(9.6.4.2)を使用して明示的または暗黙的に表されます)。具体的には、次のようになります。

    • TCACの保持期間がjava.lang.annotation.RetentionPolicy.SOURCEである場合、TAの保持期間はjava.lang.annotation.RetentionPolicy.SOURCEです。

    • TCACの保持期間がjava.lang.annotation.RetentionPolicy.CLASSである場合、TAの保持期間はjava.lang.annotation.RetentionPolicy.CLASSまたはjava.lang.annotation.RetentionPolicy.SOURCEです。

    • TCACの保持期間がjava.lang.annotation.RetentionPolicy.RUNTIMEである場合、TAの保持期間はjava.lang.annotation.RetentionPolicy.SOURCEjava.lang.annotation.RetentionPolicy.CLASSまたはjava.lang.annotation.RetentionPolicy.RUNTIMEです。

  4. 少なくともTCACと同じ種類のプログラム要素にTAを適用できます(9.6.4.1)。具体的には、TAを適用できるプログラム要素の種類がセットm1によって示され、TCACを適用できるプログラム要素の種類がセットm2によって示される場合、m2内のそれぞれの種類がm1に出現する必要があります。ただし、次の場合は除きます。

    • m2内の種類がjava.lang.annotation.ElementType.ANNOTATION_TYPEである場合は、java.lang.annotation.ElementType.ANNOTATION_TYPEjava.lang.annotation.ElementType.TYPEまたはjava.lang.annotation.ElementType.TYPE_USEのうちの少なくとも1つがm1に出現する必要があります。

    • m2内の種類がjava.lang.annotation.ElementType.TYPEである場合は、java.lang.annotation.ElementType.TYPEまたはjava.lang.annotation.ElementType.TYPE_USEのうちの少なくとも1つがm1に出現する必要があります。

    • m2内の種類がjava.lang.annotation.ElementType.TYPE_PARAMETERである場合は、java.lang.annotation.ElementType.TYPE_PARAMETERまたはjava.lang.annotation.ElementType.TYPE_USEのうちの少なくとも1つがm1に出現する必要があります。

    この条項によって、注釈インタフェースは、それを適用できるプログラム要素の種類の一部でのみ繰返し可能にすることができるというポリシーが実装されます。

  5. java.lang.annotation.Documentedに対応する(メタ)注釈がTAの宣言に含まれている場合、java.lang.annotation.Documentedに対応する(メタ)注釈がTCACの宣言に含まれている必要があります。

    TA@DocumentedでないときにTCAC@Documentedであることは許容されます。

  6. java.lang.annotation.Inheritedに対応する(メタ)注釈がTAの宣言に含まれている場合、java.lang.annotation.Inheritedに対応する(メタ)注釈がTCACの宣言に含まれている必要があります。

    TA@InheritedでないときにTCAC@Inheritedであることは許容されます。

注釈TインタフェースAに、Tの包含注釈インタフェースでない型をvalue要素が示す@Repeatable注釈を使用して(メタ)注釈が付けられている場合、コンパイル時にエラーが発生します。

例9.6.3-1.形式が正しくない包含注釈インタフェース

次の宣言を考えてみましょう。

import java.lang.annotation.Repeatable;

@Repeatable(FooContainer.class)
@interface Foo {}

@interface FooContainer { Object[] value(); }

Foo@Repeatableを使用して、FooContainerをその包含注釈インタフェースとして指定しようとしていますが、実際は、FooContainerFooの包含注釈インタフェースではないため、Foo宣言をコンパイルすると、コンパイル時にエラーが生成されます。(FooContainer.value()の戻り型はFoo[]ではありません。)

@Repeatable注釈を繰り返すことはできないため、繰返し可能な注釈インタフェースで指定できる包含注釈インタフェースは1つのみです。

複数の包含注釈インタフェースの指定を許可すると、繰返し可能な注釈インタフェースの複数の注釈が論理的にコンテナ注釈(9.7.5)に置き換えられるときに、コンパイル時に望ましくない選択が行われます。

注釈インタフェースは、最大1つの注釈インタフェースの包含注釈インタフェースにすることができます。

このことは、注釈TインタフェースAの宣言でTCACという包含注釈インタフェースを指定する場合、TCACvalue()メソッドの戻り型はTAを含む、具体的にはT[]A[]であるという要件によって暗黙的に規定されています。

注釈インタフェースでそれ自体をその包含注釈インタフェースとして指定することはできません。

このことは、包含注釈インタフェースvalue()メソッドに関する要件によって暗黙的に規定されています。具体的には、注釈インタフェースAでそれ自体を(@Repeatableによって)その包含注釈インタフェースとして指定する場合、Avalue()メソッドの戻り型はA[]である必要があります。しかし、注釈インタフェースはその要素(9.6.1)内でそれ自体を参照できないため、これによってコンパイル時にエラーが発生します。より一般的には、注釈インタフェースの循環宣言は不正であるため、2つの注釈インタフェースで相互をその包含注釈インタフェースとして指定することはできません。

注釈TCインタフェースACは、注釈TインタフェースAの包含注釈インタフェースであると同時に、独自の包含注釈TC 'インタフェースAC 'を持つこともできます。つまり、包含注釈インタフェース自体を繰返し可能な注釈インタフェースにすることができます。

例9.6.3-2.注釈を繰り返すことができる場所の制限

宣言がjava.lang.annotation.ElementType.TYPEというターゲットを示す注釈は、宣言がjava.lang.annotation.ElementType.ANNOTATION_TYPEというターゲットを示す注釈と少なくとも同じ数の場所に出現できます。たとえば、繰返し可能な注釈および包含注釈インタフェースの次のような宣言があるとします。

import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;

@Target(ElementType.TYPE)
@Repeatable(FooContainer.class)
@interface Foo {}

@Target(ElementType.ANNOTATION_TYPE)
@interface FooContainer {
    Foo[] value();
}

@Fooはすべてのクラスまたはインタフェース宣言に出現できますが、@FooContainerは注釈インタフェース宣言にのみ出現できます。したがって、次の注釈インタフェース宣言は正当です。

@Foo @Foo
@interface Anno {}

一方、次のインタフェース宣言は不正です。

@Foo @Foo
interface Intf {}

さらに広げて、Fooが繰返し可能な注釈インタフェースで、FooContainerがその包含注釈インタフェースである場合を考えてみましょう。

例9.6.3-3.繰返し可能な包含注釈インタフェース

次の宣言は正当です。

import java.lang.annotation.Repeatable;
// Foo: Repeatable annotation type
// Foo: Repeatable annotation interface
@Repeatable(FooContainer.class)
@interface Foo { int value(); }
// FooContainer: Containing annotation type of Foo
//               Also a repeatable annotation type itself
// FooContainer: Containing annotation interface of Foo
//               Also a repeatable annotation interface itself
@Repeatable(FooContainerContainer.class)
@interface FooContainer { Foo[] value(); }
// FooContainerContainer: Containing annotation type of FooContainer
// FooContainerContainer: Containing annotation interface of FooContainer
@interface FooContainerContainer { FooContainer[] value(); }

つまり、インタフェースが包含注釈インタフェースである注釈自体を繰り返すことができます。

@FooContainer({@Foo(1)}) @FooContainer({@Foo(2)})
class Test {}

繰返し可能と包含の両方である注釈インタフェースには、繰返し可能な注釈インタフェースの注釈と包含注釈インタフェースの注釈の混在に関するルールが適用されます(9.7.5)。たとえば、複数の@Foo注釈を複数の@FooContainer注釈と一緒に記述することはできません。また、複数の@FooContainer注釈を複数の@FooContainerContainer注釈と一緒に記述することもできません。ただし、FooContainerContainer注釈インタフェース自体が繰返し可能である場合は、複数の@Foo注釈を複数の@FooContainerContainer注釈と一緒に記述できます。

9.6.4 事前定義済注釈インタフェース

Java SEプラットフォームのライブラリには、いくつかの注釈インタフェースが事前定義されています。これらの事前定義済注釈インタフェースの中には、特別なセマンティクスを持つものがあります。このセクションでは、これらのセマンティクスについて規定します。このセクションは、ここに含まれている事前定義済注釈の完全な仕様を示すものではありません。それは該当するAPI仕様の役割となります。ここでは、JavaコンパイラまたはJava仮想マシンの実装側で特別な動作を必要とするセマンティクスのみを規定します。

9.6.4.1 @Target

java.lang.annotation.Targetの注釈を注釈インタフェースTの宣言で使用して、T適用できるコンテキストを指定します。java.lang.annotation.Targetは、コンテキストを指定するための、型java.lang.annotation.ElementType[]の1つの要素valueを持ちます。

注釈インタフェースは、注釈が宣言に適用される宣言コンテキストで適用可能である場合もあれば、宣言および式で使用される型に注釈が適用される型コンテキストで適用可能である場合もあります。

宣言コンテキストは9つあり、それぞれがjava.lang.annotation.ElementTypeのenum定数に対応しています。

  1. モジュール宣言(7.7)

    java.lang.annotation.ElementType.MODULEに対応しています

  2. パッケージ宣言(7.4.1)

    java.lang.annotation.ElementType.PACKAGEに対応しています

  3. 型宣言: クラス、インタフェース、enumおよび注釈宣言(8.1.19.1.18.59.58.99.6)

    java.lang.annotation.ElementType.TYPEに対応しています

    また、注釈宣言はjava.lang.annotation.ElementType.ANNOTATION_TYPEに対応しています

  4. メソッド宣言(注釈インタフェースの要素を含む) (8.4.39.49.6.1)

    java.lang.annotation.ElementType.METHODに対応しています

  5. コンストラクタ宣言(8.8.3)

    java.lang.annotation.ElementType.CONSTRUCTORに対応しています

  6. 汎用クラス、インタフェース、メソッドおよびコンストラクタの型パラメータ宣言(8.1.29.1.28.4.48.8.4)

    java.lang.annotation.ElementType.TYPE_PARAMETERに対応しています

  7. フィールド宣言(enum定数を含む) (8.3.19.38.9.1)

    java.lang.annotation.ElementType.FIELDに対応しています

  8. 仮パラメータおよび例外パラメータ宣言(8.4.19.414.20)

    java.lang.annotation.ElementType.PARAMETERに対応しています

  9. ローカル変数宣言(for文のループ変数およびtry-with-resources文のリソース変数を含む) (14.414.14.114.14.214.20.3)

    java.lang.annotation.ElementType.LOCAL_VARIABLEに対応しています

型コンテキストは16あり(4.11)、これらはすべて、java.lang.annotation.ElementTypeのenum定数TYPE_USEによって表されます。

java.lang.annotation.Targetの注釈のvalue要素内に同じenum定数が複数回出現すると、コンパイル時にエラーが発生します。

java.lang.annotation.Targetの注釈が注釈インタフェースTの宣言に存在しない場合、Tは、9個の宣言コンテキストすべてと16個の型コンテキストすべてで適用可能です。

9.6.4.2 @Retention

注釈はソース・コードにのみ存在できます。あるいは、クラスまたはインタフェースのバイナリ形式に存在することもあります。バイナリ形式に存在する注釈は、Java SEプラットフォームのリフレクション・ライブラリを介して実行時に使用できることもあれば、使用できないこともあります。注釈インタフェースjava.lang.annotation.Retentionは、これらの可能性から選択するために使用されます。

注釈a注釈インタフェースTに対応し、java.lang.annotation.Retentionに対応する(メタ)注釈mTに含まれている場合、次のようになります。

java.lang.annotation.Retentionに対応する(メタ)注釈mTに含まれていない場合、Javaコンパイラは、値がjava.lang.annotation.RetentionPolicy.CLASSである要素を持つそのようなメタ注釈mが含まれている場合と同様にTを処理する必要があります。

9.6.4.3 @Inherited

注釈インタフェースjava.lang.annotation.Inheritedは、指定した注釈インタフェースに対応するクラスCの注釈がCのサブクラスによって継承されることを示す場合に使用します。

9.6.4.4 @Override

プログラマは、メソッド宣言をオーバーライドするときにそれをオーバーロードすることがあり、微妙な問題につながります。注釈インタフェースOverrideは、こうした問題の早期検出をサポートします。

従来の例は、equalsメソッドに関するものです。プログラマは、クラスFooで次のように記述します。

public boolean equals(Foo that) { ... }

実際に記述しようとした内容は次のとおりです。

public boolean equals(Object that) { ... }

これは完全に正当ですが、クラスFooObjectからequals実装を継承するため、微妙なバグの原因となる可能性があります。

クラスまたはインタフェースTのメソッド宣言に@Overrideの注釈が付けられているが、そのメソッドが、Tのスーパー・タイプで宣言されたメソッドをTからオーバーライドしない場合(8.4.8.19.4.1.1)、またはObjectpublicメソッドとオーバーライド等価でない場合(4.3.28.4.2)、コンパイル時にエラーが発生します。

この動作は、Java SE 5.0とは異なります。Java SE 5.0では、@Overrideによってコンパイル時にエラーが発生するのは、スーパーインタフェースからメソッドを実装し、スーパークラスにも存在しないメソッドに適用された場合のみでした。

publicメソッドのオーバーライドに関する条項の背景には、インタフェースでの@Overrideの使用があります。次の宣言を考えてみましょう。

class Foo     { @Override public int hashCode() {..} }
interface Bar { @Override int hashCode(); }

Foo.hashCodeはメソッドObject.hashCodeFooからオーバーライドするため、クラス宣言での@Overrideの使用は、最初の条項によって正当です。

インタフェース宣言については、インタフェースがObjectをスーパー・タイプとして持たず、Objectpublicメンバーに対応するpublic abstractメンバーをインタフェースが持つとします(9.2)。インタフェースでそれらを明示的に宣言する(つまり、Objectpublicメソッドとオーバーライド等価であるメンバーを宣言する)ことを選択した場合、インタフェースはそれらをオーバーライドするとみなされ、@Overrideの使用は許可されます。

一方、cloneメソッドで@Overrideを使用しようとするインタフェースを考えてみましょう。(この例では、finalizeを使用することもできます)

interface Quux { @Override Object clone(); }

Object.clonepublicでないため、Quuxで暗黙的に宣言されたcloneというメンバーはありません。したがって、Quux内のcloneの明示的な宣言は他のメソッドを実装するとみなされず、@Overrideの使用は誤りです。(Quux.clonepublicであるという事実は関係ありません。)

対照的に、cloneを宣言するクラス宣言は単純にObject.cloneをオーバーライドするため、@Overrideを使用できます。

class Beep { @Override protected Object clone() {..} }
9.6.4.5 @SuppressWarnings

Javaコンパイラは、有用な「lintのような」警告を徐々に発行できるようになってきています。こうした警告の使用を促進するためには、プログラマが警告が不適切であることがわかっているときに、プログラムの一部で警告を無効にするためのなんらかの方法が必要です。

注釈インタフェースSuppressWarningsは、本来ならJavaコンパイラによって発行される警告のプログラマによる制御をサポートします。これは、Stringの配列である1つの要素を定義します。

宣言に@SuppressWarnings(value = {S1, ..., Sk})の注釈が付けられている場合、注釈が付けられた宣言またはその一部の結果として、S1 ... Skのいずれかによって指定された警告が生成されると、Javaコンパイラはその警告を抑制する(つまり、報告しないようにする)必要があります。

Javaプログラミング言語では、@SuppressWarningsで指定できる4種類の警告が定義されています。

他の文字列は、標準以外の警告を指定します。Javaコンパイラは、認識しないこのような文字列を無視する必要があります。

コンパイラ・ベンダーは、@SuppressWarningsについてサポートする文字列を文書化することと、同じ文字列が複数のコンパイラにわたって認識されるようにするために協力し合うことを奨励されています。

9.6.4.6 @Deprecated

プログラマは、特定のプログラム要素(モジュール、クラス、インタフェース、フィールド、メソッドおよびコンストラクタ)について、それらが危険であったり、より効果的な代替手段が存在するために使用を推奨されないことがあります。注釈インタフェースDeprecatedを使用すると、そうしたプログラム要素の使用についてコンパイラで警告できます。

非推奨のプログラム要素は、宣言に@Deprecatedの注釈が付けられているモジュール、クラス、インタフェース、フィールド、メソッドまたはコンストラクタです。プログラム要素の非推奨の形態は、注釈のforRemoval要素の値によって異なります。

プログラム要素の宣言で(明示的に宣言されているか、暗黙的に宣言されているかに関係なく)通常の非推奨プログラム要素が使用されている(オーバーライドされている、呼び出されている、または名前で参照されている)場合、Javaコンパイラは非推奨警告を生成する必要があります。ただし、次の場合は除きます。

プログラム要素の宣言で(明示的に宣言されているか、暗黙的に宣言されているかに関係なく)廃止予定の非推奨プログラム要素が使用されている(オーバーライドされている、呼び出されている、または名前で参照されている)場合、Javaコンパイラは削除警告を生成する必要があります。ただし、次の場合は除きます。

廃止予定の非推奨には十分な緊急性があるため、廃止予定の非推奨要素が使用されていると、使用している側の要素自体が非推奨であっても、削除警告が生成されます。両方の要素が同時に削除されるという保証がないためです。警告を閉じて要素の使用を続行するには、プログラマは、@SuppressWarnings注釈を使用して、そのリスクを手動で確認する必要があります。

次の場合、非推奨警告または削除警告は生成されません。

パッケージをエクスポートする、または開くモジュール宣言は、通常、パッケージの宣言を制御するのと同じプログラマまたはチームによって制御されます。そのため、パッケージがモジュール宣言によってエクスポートされたり、開かれたときに、パッケージ宣言に@Deprecatedの注釈が付けられていると警告することにほとんど利点はありません。一方、パッケージをフレンド・モジュールに対してエクスポートする、または開くモジュール宣言は、通常、フレンド・モジュールを制御するのと同じプログラマまたはチームによって制御されるわけではありません。単にパッケージをエクスポートしたり、開いても、モジュール宣言がフレンド・モジュールに依存するようになるわけではないため、フレンド・モジュールが非推奨である場合に警告することにほとんど価値はありません。モジュール宣言のプログラマは、ほとんどの場合、このような警告を抑制しようと考えます。

非推奨警告または削除警告が発生する可能性がある唯一の暗黙的な宣言は、コンテナ注釈(9.7.5)です。つまり、Tが繰返し可能な注釈インタフェースで、TCがその包含注釈インタフェースであり、かつTCが非推奨である場合、@T注釈を繰り返すと、警告が発生します。この警告は、暗黙的な@TCコンテナ注釈によるものです。対応する繰返し可能な注釈インタフェースを非推奨にすることなく、包含注釈インタフェースを非推奨にすることはお薦めしません。

9.6.4.7 @SafeVarargs

非reifiable要素型(4.7)の可変引数パラメータは、ヒープ汚染(4.12.2)の原因となり、コンパイル時に未チェック警告(5.1.9)を生成する可能性があります。可変引数メソッドの本体が可変引数パラメータに関して正常に動作する場合、このような警告は有益ではありません。

注釈インタフェースSafeVarargsを使用して、メソッドまたはコンストラクタ宣言に注釈を付けると、プログラマは、Javaコンパイラが可変引数メソッドまたはコンストラクタの宣言や呼出しについて未チェック警告を報告しないようにすることができます。そうしない場合、可変引数パラメータが非reifiable要素型を持つことが原因で、コンパイラはそのような警告を報告します。

注釈@SafeVarargsは、可変引数メソッド自体の宣言に関係する未チェック警告(8.4.1)に加え、メソッド呼出し式でも未チェック警告を抑制するため、その影響は局所的ではありません。対照的に、注釈@SuppressWarnings("unchecked")は、メソッドの宣言に関係する未チェック警告のみを抑制するため、その影響は局所的です。

@SafeVarargsの正規のターゲットは、宣言が次のもので始まる、java.util.Collections.addAllのようなメソッドです。

public static <T> boolean
  addAll(Collection<? super T> c, T... elements)

この可変引数パラメータには、非reifiableである宣言された型T[]があります。ただし、このメソッドは基本的に、単に入力配列から読み取って、要素をコレクションに追加します。これはどちらも、配列に関して安全な操作です。したがって、java.util.Collections.addAllのメソッド呼出し式でのコンパイル時の未チェック警告は、ほぼ間違いなく誤りであり、有益ではありません。メソッド宣言に@SafeVarargsを適用すると、メソッド呼出し式でのこうした未チェック警告の生成を回避できます。

注釈@SafeVarargsを使用して固定引数メソッドまたはコンストラクタ宣言に注釈が付けられている場合、コンパイル時にエラーが発生します。

注釈@SafeVarargsを使用して、staticでもfinalでもprivateでもない可変引数メソッド宣言に注釈が付けられている場合、コンパイル時にエラーが発生します。

@SafeVarargsは、staticメソッド、finalまたはprivate (あるいはその両方)であるインスタンス・メソッド、およびコンストラクタにのみ適用可能であるため、メソッドのオーバーライドが発生する場合、この注釈は使用できません。注釈の継承はクラスの注釈についてのみ機能する(メソッド、インタフェースまたはコンストラクタの注釈については機能しない)ため、クラス内のインスタンス・メソッドを介して、あるいはインタフェースを介して@SafeVarargsスタイルの注釈を渡すことはできません。

9.6.4.8 @Repeatable

注釈インタフェースjava.lang.annotation.Repeatable繰返し可能な注釈インタフェースの宣言で使用して、その包含注釈インタフェース(9.6.3)を指定します。

TCを示す、Tの宣言の@Repeatableメタ注釈は、TCTの包含注釈インタフェースにするには十分ではありませんTCTの包含注釈インタフェースとみなされるための様々な整形式ルールがあります。

9.6.4.9 @FunctionalInterface

注釈インタフェースFunctionalInterfaceは、インタフェースが機能インタフェース(9.8)として想定されていることを示す場合に使用します。これにより、機能すると想定されているインタフェースに出現したり、継承される不適切なメソッド宣言の早期検出が容易になります。

インタフェース宣言に@FunctionalInterfaceの注釈が付けられているが、実際は機能インタフェースでない場合、コンパイル時にエラーが発生します。

インタフェースの中には付随的に機能するものもあるため、機能インタフェースのすべての宣言に@FunctionalInterfaceの注釈を付けることは必要ないか、望ましくありません。

9.7 注釈

注釈は、プログラムのコンストラクトに情報を関連付けるマーカーですが、実行時には影響しません。注釈は、注釈インタフェース(9.6)の特定の呼出しインスタンスを示し、通常はそのインタフェースの要素に値を提供します。

3種類の注釈があります。1つ目の種類が最も一般的で、他の種類は単に1つ目の種類の簡略表現です。

注釈:
NormalAnnotation
MarkerAnnotation
SingleElementAnnotation

標準注釈は9.7.1、マーカー注釈は9.7.2、単一要素注釈は9.7.3で説明されています。9.7.4で説明されているとおり、注釈はプログラムの様々な構文上の場所に出現します。9.7.5で説明されているとおり、1つの場所に出現できる同じインタフェースの注釈の数は、その型インタフェース宣言によって決定します。

9.7.1 標準注釈

標準注釈は、注釈インタフェースの名前と、オプションでカンマ区切りの要素と値のペアのリストを指定します。各ペアには、注釈インタフェースの要素(9.6.1)に関連付けられた要素値が含まれます。

NormalAnnotation:
@ TypeName ( [ElementValuePairList] )
ElementValuePairList:
ElementValuePair {, ElementValuePair}
ElementValuePair:
Identifier = ElementValue
ElementValue:
ConditionalExpression
ElementValueArrayInitializer
Annotation
ElementValueArrayInitializer:
{ [ElementValueList] [,] }
ElementValueList:
ElementValue {, ElementValue}

アットマーク(@)はそれ自体へのトークンであることに注意してください(3.11)。それとTypeNameの間に空白を入れることは可能ですが、スタイルの面からお薦めできません。

TypeNameは、注釈に対応する注釈インタフェースを指定します。それが、インタフェースの注釈であると言い表されます。

TypeNameは、アクセス可能な注釈インタフェース(6.6)に名前を付ける必要があります。そうでない場合、コンパイル時にエラーが発生します。

要素と値のペアの識別子は、注釈インタフェースのいずれかの要素(つまり、メソッド)の単純名である必要があります。そうでない場合、コンパイル時にエラーが発生します。

このメソッドの戻り型によって、要素と値のペアの要素型が定義されます。

要素型が配列型である場合、要素と値のペアの要素値を指定するのに中カッコを使用する必要はありません。要素値がElementValueArrayInitializerではない場合、要素値である唯一の要素を持つ配列値が要素に関連付けられます。要素値がElementValueArrayInitializerである場合、ElementValueArrayInitializerによって表される配列値が要素に関連付けられます。

要素型が、要素値に対応していない場合、コンパイル時にエラーが発生します。次のいずれかが当てはまる場合のみ、要素型Tは要素値Vに対応しています。

Tが配列型でも注釈インタフェース型でもない場合、要素値はConditionalExpression ([15.25])である必要があります。Expressionなどのより一般的なプロダクションではなく、ConditionalExpressionを使用することは、要素値としての割当て式を防止する構文上のトリックです。割当て式は定数式ではないため、これは、プリミティブ型要素またはString型要素の対応する要素値にはできません。

正式にはElementValueは注釈またはクラス・リテラルになるため、FP-strict (15.4)として言及するのは正しくありません。ElementValueが定数式、定数式の配列、または要素値が定数式として(再帰的に)検出された注釈の場合は、正式ではありませんが、FP-strictとして言及する場合があります。要するに、すべての定数式はFP-strictです。

標準注釈には、対応する注釈インタフェースのすべての要素(デフォルト値が指定された要素を除く)に対する要素と値のペアが含まれる必要があります。そうでない場合、コンパイル時にエラーが発生します。

標準注釈には、デフォルト値が指定された要素の要素と値のペアを含めることができます(必須ではありません)。

注釈の要素と値のペアは、注釈宣言の対応する要素と同じ順序で提示されることは慣例ですが、必須ではありません。

注釈宣言の注釈は、メタ注釈と呼ばれます。

TインタフェースAの注釈は、TインタフェースA自体の宣言のメタ注釈として出現できます。より一般的には、「注釈付け」関係の推移閉包の循環性が許可されます。

たとえば、インタフェースTのメタ注釈を持つ注釈インタフェースSの宣言に注釈を付けること、およびインタフェースSのメタ注釈を持つTの独自の宣言に注釈を付けることは、正当です。事前定義済の注釈インタフェースには、そのような循環性がいくつか含まれます。

例9.7.1-1.標準注釈

次に、9.6.1の注釈インタフェースを使用した標準注釈の例を示します。

@RequestForEnhancement(
    id       = 2868724,
    synopsis = "Provide time-travel functionality",
    engineer = "Mr. Peabody",
    date     = "4/1/2004"
)
public static void travelThroughTime(Date destination) { ... }

次に、9.6.2の注釈インタフェースを使用した、デフォルト値を活用する標準注釈の例を示します。

@RequestForEnhancement(
    id       = 4561414,
    synopsis = "Balance the federal budget"
)
public static void balanceFederalBudget() {
    throw new UnsupportedOperationException("Not implemented");
}

9.7.2 マーカー注釈

マーカー注釈は、マーカー注釈インタフェース(9.6.1)と使用するために設計された簡略表現です。

MarkerAnnotation:
@ TypeName

これは次の標準注釈の簡略表現です。

@*TypeName*()

すべての要素にデフォルト値があるかぎり(9.6.2)、要素を持つ注釈インタフェースにマーカー注釈を使用することは正当です。

例9.7.2-1.マーカー注釈

次に、9.6.1Preliminaryマーカー注釈インタフェースを使用した例を示します。

@Preliminary public class TimeTravel { ... }

9.7.3 単一要素注釈

単一要素注釈は、単一要素注釈インタフェース(9.6.1)と使用するために設計された簡略表現です。

SingleElementAnnotation:
@ TypeName ( ElementValue )

これは次の標準注釈の簡略表現です。

@*TypeName*(value = *ElementValue*)

1つの要素にvalueという名前が付けられ、かつ他のすべての要素にデフォルト値があるかぎり(9.6.2)、複数の要素を持つ注釈インタフェースに単一要素注釈を使用することは正当です。

例9.7.3-1.単一要素注釈

次の注釈はすべて、9.6.1の単一要素注釈インタフェースを使用します。

次に、単一要素注釈の例を示します。

@Copyright("2002 Yoyodyne Propulsion Systems, Inc.")
public class OscillationOverthruster { ... }

次に、配列値の単一要素注釈の例を示します。

@Endorsers({"Children", "Unscrupulous dentists"})
public class Lollipop { ... }

次に、単一要素配列値の単一要素注釈の例を示します。(中カッコは省略されています)

@Endorsers("Epicurus")
public class Pleasure { ... }

次に、制限付きのワイルドカードによって値が制約されているClass型の要素を持つ単一要素注釈の例を示します。

class GorgeousFormatter implements Formatter { ... }

@PrettyPrinter(GorgeousFormatter.class)
public class Petunia { ... }

// Illegal; String is not a subtype of Formatter
@PrettyPrinter(String.class)
public class Begonia { ... }

次に、標準注釈を含む単一要素注釈を使用したの例を示します。

@Author(@Name(first = "Joe", last = "Hacker"))
public class BitTwiddle { ... }

次に、注釈宣言内で定義されたenumクラスを使用する単一要素注釈の例を示します。

@Quality(Quality.Level.GOOD)
public class Karma { ... }

9.7.4 注釈が出現する可能性がある場所

宣言注釈は、宣言に適用され、その宣言によって表される宣言コンテキスト(9.6.4.1)に独自の型注釈インタフェースを適用できる注釈、またはクラス、インタフェース、enum、注釈型または型パラメータ宣言に適用され、型コンテキスト(4.11)に独自の型注釈インタフェースを適用できる注釈です。

型注釈は、型(または型の任意の部分)に適用され、型コンテキストに独自の型注釈インタフェースを適用できる注釈です。

たとえば、次のフィールド宣言が与えられた場合、

@Foo int f;

@Fooは、Foo@Target(ElementType.FIELD)によってメタ注釈が付けられた場合はfに対する宣言注釈であり、Foo@Target(ElementType.TYPE_USE)によってメタ注釈が付けられた場合はintに対する型注釈です。@Fooは同時に宣言注釈と型注釈の両方にすることができます。

型注釈は、配列型またはそのコンポーネント型(10.1)に適用できます。たとえば、ABおよびC@Target(ElementType.TYPE_USE)によってメタ注釈が付けられた注釈インタフェースであるとするときに、次のフィールド宣言が与えられた場合、

@C int @A [] @B [] f;

@Aは配列型int[][]に適用され、@Bはコンポーネント型int[]に適用され、@Cは要素型intに適用されます。その他の例については、次の情報を参照してください 10.2

この構文の重要な特性は、2つの宣言においては配列レベルの数のみが異なり、型の左側注釈が同じ型を参照している点です。たとえば、@Cは、次のすべての宣言内の型intに適用されます。

@C int f;
@C int[] f;
@C int[][] f;

宣言注釈を他のすべての修飾子の前に書き、型注釈をそれが適用される型の直前に書くことは慣例ですが、必須ではありません。

プログラム構文で、一見すると宣言または型、あるいはその両方に適用されるような場所に注釈が出現する場合があります。これは、宣言されたエンティティの型の直前に修飾子がある次の5つの宣言コンテキストのいずれかで考えられます。

Javaプログラミング言語の文法では、これらの場所にある注釈が宣言(8.3)の修飾子として明確に取り扱われますが、これは純粋に構文上だけのものです。注釈が宣言または宣言されたエンティティの型のどちらに適用されるか、つまり、注釈が宣言注釈または型注釈のどちらであるかは、注釈のインタフェースの適用性によって決まります。

前述の2番目のと3番目のケースでは、注釈に最も近い型は、次のように決定されます。

TインタフェースAの注釈が次の構文上の修飾子である場合、コンパイル時にエラーが発生します。

これら9つの条項のうち5つの条項で「...または型コンテキスト」と記載されているのは、これらが、注釈を宣言または宣言されたエンティティの型に適用できるように見える5つの構文上の場所を特徴付けているからです。また、クラスおよびインタフェース、enumおよび注釈型宣言、さらに型パラメータ宣言に関する9つの条項のうち2つの条項で「...または型コンテキスト」と記載されているのは、インタフェース@Target(ElementType.TYPE_USE)がメタ注釈として付けられている(ため、型コンテキストに適用可能である)注釈をクラス、インタフェースまたは型パラメータ宣言に適用する方が便利である可能性があるからです。

次の両方が当てはまる場合、型注釈は許容されます。

2番目の条項では、Outerに囲まれたネストしたクラス内でOuter.thisが正当である場合、Outerは、実行時に一部のオブジェクトの型を表すため、注釈が付けられる可能性があると考えられます。その一方で、Outer.thisが正当ではない場合(これが出現するクラスには実行時にOuterの包含インスタンスがないため)、Outerは、論理的に単なる名前(完全修飾された型名のパッケージ名のコンポーネントと同種)であるため、注釈が付けられない可能性があります。

たとえば、次のプログラムの場合、Bには字句的な包含インスタンスがないため(8.5.1)、Bの本体内にA.thisを作成することはできません。したがって、Aは論理的に型ではなく単なる名前であるため、@Fooを型A.B内のAに適用することはできません。

@Target(ElementType.TYPE_USE)
@interface Foo {}

class A {
    static class B {}
}

@Foo A.B x;  // Illegal 

その一方で、次のプログラムの場合、Dの本体内にC.thisを作成できます。したがって、Cは実行時の一部のオブジェクトの型を表すため、@Fooを型C.D内のCに適用できます。

@Target(ElementType.TYPE_USE)
@interface Foo {}

class Test {
    static class C {
        class D {}
    }

    @Foo C.D x;  // Legal 
}

TインタフェースAの注釈が型コンテキスト内の型の最も外側のレベルに適用されるときに、型コンテキスト、または同じ構文上の場所を占有する宣言コンテキスト(存在する場合)にTAを適用できない場合、コンパイル時にエラーが発生します。

TインタフェースAの注釈が型コンテキスト内の型の一部(つまり、最も外側のレベルではない)に適用されるときに、型コンテキストにTAを適用できない場合、コンパイル時にエラーが発生します。

TインタフェースAの注釈が型コンテキスト内の型(または型の一部)に適用されるときに、型コンテキストにTAを適用できできるが、注釈が許容されない場合、コンパイル時にエラーが発生します。

たとえば、@Target(ElementType.TYPE_USE)によってのみメタ注釈が付けられた注釈インタフェースTAがあるとします。@TAが最も近い単純名がパッケージ名として分類されているため、語句@TA java.lang.Objectおよびjava.@TA lang.Objectは不正です。一方、java.lang.@TA Objectは不正です。

不正な語句は「どこでも」不正です。注釈パッケージ名に対する禁止事項は広範に適用されます。具体的には、単なる型コンテキストである場所(class ... extends @TA java.lang.Object {...}など)、および宣言コンテキストと型コンテキストの両方である場所(@TA java.lang.Object f;など)に適用されます。(クラス、パッケージおよび型パラメータ宣言では単純名のみが使用されるため、パッケージ名に注釈を付けられる単なる宣言コンテキストである場所は存在しません。)

TAに追加的に@Target(ElementType.FIELD)のメタ注釈が付けられた場合、語句@TA java.lang.Objectは、宣言コンテキストと型コンテキストの両方である場所(フィールド宣言@TA java.lang.Object f;など)で正当になります。ここでは、TAはフィールド宣言コンテキストに適用できるため、@TAは(型java.lang.Objectに対してではなく) fの宣言に対して適用されるとみなされます。

9.7.5 同じインタフェースの複数の注釈

同じTインタフェースAの複数の注釈が宣言コンテキストまたは型コンテキストに出現する場合、コンパイル時にエラーが発生します。ただし、TAが繰返し可能であり(9.6.3)、TATAの包含注釈インタフェースの両方が宣言コンテキストまたは型コンテキストに適用可能である場合は除きます(9.6.4.1)。

同じインタフェースの複数の注釈が連続して出現することは慣例ですが、必須ではありません。

宣言コンテキストまたは型コンテキストが、繰返し可能な注釈TインタフェースAの複数の注釈を持つ場合、コンテキストは、TインタフェースAの明示的に宣言された注釈を持たずに、TAの包含注釈インタフェースの暗黙的に宣言された注釈を1つ持つかのようになります。

暗黙的に宣言された注釈はコンテナ注釈と呼ばれ、コンテキストに出現したTインタフェースAの複数の注釈はベース注釈と呼ばれます。コンテナ注釈の(配列型の) value要素の要素は、コンテキストに出現した左から右の順序のすべてのベース注釈です。

宣言コンテキストまたは型コンテキストに、繰返し可能な注釈TインタフェースAの複数の注釈と、TAの包含注釈インタフェースの注釈が存在する場合、コンパイル時にエラーが発生します。

つまり、そのコンテナと同じインタフェースの注釈も出現する注釈を繰り返すことはできません。これにより、次のような洗練されないコードは禁止されています。

@Foo(0) @Foo(1) @FooContainer({@Foo(2)})
class A {}

このコードが正当であるとした場合は、複数のレベルの包含が必要になります。まず、インタフェースFooの注釈が、インタフェースFooContainerの暗黙的に宣言されたコンテナ注釈に含まれ、さらにその注釈と、インタフェースFooContainerの明示的に宣言された注釈が、別の暗黙的に宣言された注釈に含まれます。Javaプログラミング言語の設計者は、この複雑さは望ましくないと判断しています。インタフェースFooの注釈を、明示的な@FooContainer注釈の@Foo(2)と一緒に出現した場合と同様に処理するという別のアプローチは、反射型プログラムによる@FooContainer注釈の解釈方法を変更する可能性があるため、望ましくありません。

宣言コンテキストまたは型コンテキストに、繰返し可能な注釈TインタフェースAの1つの注釈と、TAの包含注釈インタフェースの複数の注釈が存在する場合、コンパイル時にエラーが発生します。

このルールは、次のコードを許可するために設計されています。

@Foo(1) @FooContainer({@Foo(2)})
class A {}

繰返し可能な注釈インタフェースFooの注釈が1つのみの場合、FooContainerFooの包含注釈インタフェースである場合でも、暗黙的に宣言されるコンテナ注釈はありません。ただし、次のようなインタフェースFooContainerの注釈

@Foo(1) @FooContainer({@Foo(2)}) @FooContainer({@Foo(3)})
class A {}

は、それ自体の包含注釈インタフェースによってFooContainerが繰返し可能である場合でも禁じられています。基になる繰返し可能なインタフェースの注釈が存在している場合、それ自体がコンテナである注釈を繰り返すことは適切ではありません。

第12章: 実行

12.2 クラスおよびインタフェースのロード

ロードは、その場で計算するか、より一般的には、Javaコンパイラによって以前にソース・コードから計算されたバイナリ表現を取得することによって、特定の名前を持つクラスまたはインタフェースのバイナリ形式を見つけ、そのバイナリ形式から、クラスまたはインタフェースを表すClassオブジェクトを構築するプロセスを指します。

ロードの正確なセマンティクスは、Java仮想マシン仕様、Java SE 15 Editionの第5章に記載されています。ここでは、Javaプログラミング言語の視点から、このプロセスの概要を示します。

クラスまたはインタフェースのバイナリ形式は、通常、前述のJava仮想マシン仕様、Java SE 15 Editionで説明されているclassファイル形式ですが、13.1に規定されている要件を満たしている場合は他の形式にすることができます。クラスClassLoaderのメソッドdefineClassを使用して、classファイル形式のバイナリ表現からClassオブジェクトを構築できます。

正常に動作するクラス・ローダーは、次の特性を有しています。

悪意のあるクラス・ローダーは、これらの特性に違反している可能性があります。ただし、Java仮想マシンがこれを防ぐため、型システムのセキュリティが損なわれることはありません。

これらの問題の詳細は、Java仮想マシン仕様、Java SE 15 Editionと、ACM SIGPLAN Noticesのボリューム33、ナンバー10として発行されたProceedings of OOPSLA '98 (1998年10月)のページ36-44、Dynamic Class Loading in the Java Virtual Machine (Sheng LiangおよびGilad Bracha著)を参照してください。Javaプログラミング言語の設計の基本原則として、Javaプログラミング言語で作成されたコードによっても、そのようなその他の機密性の高いシステム・クラス(ClassLoaderSecurityManagerなど)の実装によっても、実行時の型システムを覆すことはできません。

12.3 クラスおよびインタフェースのリンク

リンクは、クラスまたはインタフェースのバイナリ形式を取得し、実行時状態のJava仮想マシンに組み合せて実行できるようにするプロセスです。クラスまたはインタフェースは、常に、リンクされる前にロードされます。

リンクには、検証、準備およびシンボリック参照の解決という、3つの異なるアクティビティが関与します。

リンクの正確なセマンティクスは、Java仮想マシン仕様、Java SE 15 Editionの第5章に記載されています。ここでは、Javaプログラミング言語の視点から、このプロセスの概要を示します。

この仕様により、リンク・アクティビティが(回帰、ロードにより)実行されるタイミングについて、柔軟な実装が可能になります。初期化の前にクラスまたはインタフェースが完全に検証および準備され、リンケージ中に検出されたエラーは、プログラム内で、そのエラーに関連するクラスまたはインタフェースへのリンケージを必要とする可能性のあるプログラムによってなんらかのアクションが実行されるポイントでスローされるという、Javaプログラミング言語のセマンティクスが満たされていることが前提です。

たとえば、実装で、使用時にのみ個別にクラスまたはインタフェースの各シンボリック参照を解決するように選択することも(レイジーまたは遅延解決)、クラスの検証中に一度にそれらすべてを解決するように選択することもできます(静的解決)。これは、一部の実装で、クラスまたはインタフェースが初期化された後も解決プロセスが続く場合があることを意味します。

リンクには新しいデータ構造の割当てが関与するため、OutOfMemoryErrorで失敗する場合があります。

12.3.3 シンボリック参照の解決

クラスまたはインタフェースのバイナリ表現は、他のクラスおよびインタフェース(13.1)のバイナリ名(13.1)を使用して、他のクラスおよびインタフェースと、そのフィールド、メソッドおよびコンストラクタをシンボルで参照します。フィールドとメソッドについては、これらのシンボリック参照には、フィールドまたはメソッドがメンバーであるクラスまたはインタフェースの名前と、フィールドまたはメソッド自体の名前が、適切な型情報とともに含まれます。

シンボリック参照を使用するには事前に解決を実施する必要があり、そのときにシンボリック参照が正しいことが確認され、通常は、参照が繰り返し使用される場合にさらに効率的に処理できる直接参照に置換されます。

解決中にエラーが発生すると、エラーがスローされます。ほとんどの場合、これはクラスIncompatibleClassChangeErrorの次のいずれかのサブクラスのインスタンスになりますが、IncompatibleClassChangeErrorの他のなんらかのサブクラスのインスタンスになる場合も、クラスIncompatibleClassChangeError自体のインスタンスになる場合もあります。このエラーは、型へのシンボリック参照(直接または間接的)を使用するプログラム内のいずれかのポイントでスローされる可能性があります。

これは「クラスまたはインタフェース」に変更できますが、シンボリック参照は単なるクラスおよびインタフェースへの参照というわけではありません。

...

12.5 新しいクラス・インスタンスの作成

...

新しいクラス・インスタンスが作成されるときは、常に、クラスで宣言されたすべてのインスタンス変数とクラスの各スーパークラスで宣言されたすべてのインスタンス変数(隠すことができるすべてのインスタンス変数を含む)のための空き領域を確保して、新しいクラス・インスタンスにメモリー領域が割り当てられます(8.3)。

...

第13章: バイナリ互換性

13.1 バイナリの形式

プログラムは、Java仮想マシン仕様、Java SE 14 Editionによって規定されているclassファイル形式に、またはJavaプログラミング言語で作成されたクラス・ローダーによってこの形式にマップできる表現にコンパイルする必要があります。

クラスまたはインタフェース宣言に対応するclassファイルには、特定の特性が必要です。これらの特性の多くは、バイナリの互換性を確保するソース・コードの変換をサポートするように明確に選択されています。必須特性は次のとおりです。

  1. クラスまたはインタフェースはバイナリ名によって名前を付ける必要があり、これは次の制約を満たす必要があります。

    • 最上位クラスまたはインタフェース(7.6)のバイナリ名は、その正規名(6.7)です。

    • メンバー・クラスまたはインタフェース(8.59.5)のバイナリ名は、それを直接包含するクラスまたはインタフェースのバイナリ名、その後に続く$、その後に続くメンバーの単純名で構成されます。

    • ローカル・クラス(14.3)のバイナリ名は、それを直接包含するクラスまたはインタフェースのバイナリ名、その後に続く$、その後に続く空でない一連の数字、その後に続くローカル・クラスの単純名で構成されます。

    • 無名クラス(15.9.5)のバイナリ名は、それを直接包含するクラスまたはインタフェースのバイナリ名、その後に続く$、その後に続く空でない一連の数字で構成されます。

    • 汎用クラスまたはインタフェース(8.1.29.1.2)によって宣言される型変数のバイナリ名は、それを直接包含するクラスまたはインタフェースのバイナリ名、その後に続く$、その後に続く型変数の単純名で構成されます。

    • 汎用メソッド(8.4.4)によって宣言される型変数のバイナリ名は、そのメソッドを宣言するクラスまたはインタフェースのバイナリ名、その後に続く$、その後に続くメソッドの記述子(JVMS §4.3.3)、その後に続く$、その後に続く型変数の単純名で構成されます。

    • 汎用コンストラクタ(8.8.4)によって宣言される型変数のバイナリ名は、そのコンストラクタを宣言するクラスのバイナリ名、その後に続く$、その後に続くコンストラクタの記述子(JVMS §4.3.3)、その後に続く$、その後に続く型変数の単純名で構成されます。

    型変数にバイナリ名が必要な理由は不明です。

  2. 別のクラスまたはインタフェースへの参照は、クラスまたはインタフェースのバイナリ名を使用するシンボリックである必要があります。

  3. 定数変数(4.12.4)であるフィールドへの参照は、コンパイル時に定数変数のイニシャライザが示す値Vに解決される必要があります。

    このようなフィールドがstaticである場合、バイナリ・ファイルのコード内にフィールドへの参照が存在しない必要があります。これには、このフィールドを宣言したクラスまたはインタフェースも含まれます。このようなフィールドは常に、初期化(12.4.2)されているものとして示される必要があり、このフィールドのデフォルトの初期値は(Vとは異なる場合)、表示されないようにします。

    このようなフィールドが非staticである場合、バイナリ・ファイルのコード内にフィールドへの参照が存在しない必要があります。ただし、このフィールドが含まれるクラス内は除きます。(インタフェースにはstaticフィールドしかないため、これはインタフェースではなくクラスです。)このクラスには、インスタンス作成時(12.5)にフィールドの値をVに設定するためのコードが必要です。

  4. クラスC内でのフィールド・アクセスを示す正当な式が与えられ、定数値ではなく、(場合によっては別個の)クラスまたはインタフェースD内で宣言されているfという名前のフィールドを参照している場合、Oracleではフィールド参照の修飾クラスまたはインタフェースを次のように定義します。

    • 式が単純名によって参照される場合に、fが現在のクラスまたはインタフェースCのメンバーである場合、TQCであるとします。それ以外の場合、TQは、fがメンバーである、最も内側の字句的な包含クラスまたはインタフェース宣言であるとします。どちらの場合も、TQが参照の修飾クラスまたはインタフェースです。

    • 参照の形式がTypeName.fであり、TypeNameがクラスまたはインタフェースを示している場合、TypeNameが示すクラスまたはインタフェースが参照の修飾クラスまたはインタフェースです。

    • 式の形式がExpressionName.fまたはPrimary.fである場合は次のようになります。

      • ExpressionNameまたはPrimaryのコンパイル時の型が交差型V1 & ... & Vn (4.9)である場合、参照の修飾クラスまたはインタフェースV1のイレイジャ(4.6)です。

      • そうでない場合、ExpressionNameまたはPrimaryのコンパイル時の型のイレイジャが参照の修飾クラスまたはインタフェースです。

    • 式の形式がsuper.fの場合、Cのスーパークラスは、参照の修飾クラスまたはインタフェースです。

    • 式の形式がTypeName.super.fの場合、TypeNameで示されるクラスのスーパークラスは、参照の修飾クラスまたはインタフェースです。

    fへの参照は、参照の修飾クラスまたはインタフェースのイレイジャ(4.6)およびフィールドの単純名fへのシンボリック参照にコンパイルする必要があります。また、型が想定どおりであるかどうかを検証者がチェックできるように、この参照には、宣言されたフィールドの型のイレイジャへのシンボリック参照も含まれている必要があります。

  5. クラスまたはインタフェースC内のメソッド呼出し式またはメソッド参照式が与えられ、(場合によっては別個の)クラスまたはインタフェースD内で宣言されている(または暗黙的な宣言されている(9.2)) mという名前のメソッドを参照している場合、Oracleではメソッド呼出しの修飾クラスまたはインタフェースを次のように定義します。

    • DObjectである場合、式の修飾クラスまたはインタフェースObjectです。

    • そうでない場合は、次のようになります。

      • メソッドが単純名によって参照されているときに、mが現在のクラスまたはインタフェースCのメンバーである場合、TQCにします。そうでない場合、TQは、mがメンバーである、最も内側にあり字句的な包含クラスまたはインタフェース宣言にします。どちらの場合も、TQがメソッド呼出しの修飾クラスまたはインタフェースです。

      • 式の形式がTypeName.mまたはReferenceType::mの場合、TypeName (またはReferenceTypeのイレイジャ(4.6))で示されるクラスまたはインタフェースは、メソッド呼出しの修飾クラスまたはインタフェースです。

      • 式の形式がExpressionName.mPrimary.mExpressionName::mまたはPrimary::mである場合、次のようになります。

        • ExpressionNameまたはPrimaryのコンパイル時の型が交差型V1 & ... & Vn (4.9)である場合、メソッド呼出しの修飾クラスまたはインタフェースV1のイレイジャです。

        • そうでない場合、ExpressionNameまたはPrimaryのコンパイル時の型のイレイジャがメソッド呼出しの修飾クラスまたはインタフェースです。

      • 式の形式がsuper.mまたはsuper::mの場合、Cのスーパークラスは、メソッド呼出しの修飾クラスまたはインタフェースです。

      • 式の形式がTypeName.super.mまたはTypeName.super::mであり、TypeNameがクラスXを示している場合、Xのスーパークラスがメソッド呼出しの修飾クラスまたはインタフェースです。また、TypeNameがインタフェースXを示している場合、Xがメソッド呼出しの修飾クラスまたはインタフェースです。

    メソッドへの参照は、コンパイル時に、呼出しの修飾クラスまたはインタフェースのイレイジャ(4.6)と、メソッドの宣言されたシグネチャ(8.4.2)のイレイジャへのシンボリック参照に解決される必要があります。メソッドのシグネチャには、15.12.3で確認されたとおりに次のすべてが含まれている必要があります。

    • メソッドの単純名

    • メソッドに対するパラメータ数

    • 各パラメータの型へのシンボリック参照

    また、メソッドへの参照には、示されるメソッドの戻り型のイレイジャへのシンボリック参照が含まれているか、示されるメソッドがvoidを宣言され、値を戻さないことを示すものが含まれている必要があります。

  6. クラス・インスタンス作成式(15.9)、明示的なコンストラクタ呼出し文(8.8.7.1)、またはクラスまたはインタフェースC内のClassType :: new (15.13)形式のメソッド参照式が与えられ、(場合によっては別個の)クラスまたはインタフェースD内で宣言されているコンストラクタmを参照している場合、Oracleでは、コンストラクタ呼出しの修飾クラスを次のように定義します。

    • 式の形式がnew D(...)ExpressionName.new D(...)Primary.new D(...)またはD :: newの場合、呼出しの修飾クラスDです。

    • 式の形式がnew D(...){...}ExpressionName.new D(...){...}またはPrimary.new D(...){...}の場合、式の修飾クラスは式のコンパイル時型によって宣言される匿名クラスです。

    • 式の形式がsuper(...)ExpressionName.super(...)またはPrimary.super(...)の場合、式の修飾クラスCの直接スーパークラスです。

    • 式の形式がthis(...)の場合、修飾
    • 式のクラスCです。

    コンストラクタへの参照は、コンパイル時に、呼出しの修飾クラスのイレイジャ(4.6)と、コンストラクタの宣言されたシグネチャ(8.8.2)へのシンボリック参照に解決される必要があります。コンストラクタのシグネチャには、次の両方が含まれている必要があります。

    • コンストラクタのパラメータ数

    • 各仮パラメータの型へのシンボリック参照

クラスまたはインタフェースのバイナリ表現には、次もすべて含まれる必要があります。

  1. これがクラスであり、Objectではない場合、このクラスの直接スーパークラスのイレイジャへのシンボリック参照。

  2. それぞれの直接スーパーインタフェースのイレイジャへのシンボリック参照(存在する場合)。

  3. フィールドの単純名およびフィールドの型のイレイジャへのシンボリック参照として与えられた、クラスまたはインタフェース内で宣言された各フィールドの仕様。

  4. これがクラスである場合、各コンストラクタの消去されたシグネチャ(前述を参照)。

  5. クラスまたはインタフェース内で宣言された各メソッド(インタフェースについては、暗黙的に宣言されたメソッドを除く(9.2))ごとに、消去されたシグネチャおよび戻り型(前述を参照)。

  6. クラスまたはインタフェースを実装するために必要なコード。

    • インタフェースについては、フィールド・イニシャライザのコード、およびブロック本体を持つ各メソッドの実装(9.4.3)。

    • クラスについては、フィールド・イニシャライザ、インスタンス・イニシャライザおよび静的イニシャライザのコード、ブロック本体を持つ各メソッドの実装(8.4.7)、および各コンストラクタの実装。

  7. それぞれのクラスまたはインタフェースに、その正規名(6.7)を回復するための十分な情報が含まれている必要があります。

  8. それぞれのメンバー・クラスまたはインタフェースに、そのソースレベルのアクセス修飾子を回復するための十分な情報が含まれている必要があります。

  9. それぞれのネストしたクラスおよびネストしたまたはインタフェースに、それを直接包含するクラスまたはインタフェース(8.1.3)へのシンボリック参照が含まれている必要があります。

  10. それぞれのクラスまたはインタフェースに、そのメンバー・クラスおよびインタフェース(8.59.5)すべてへのシンボリック参照と、そのメソッド、コンストラクタ、静的イニシャライザ、インスタンス・イニシャライザおよびフィールド・イニシャライザに出現するローカル・クラスと無名クラスその本体内で宣言された他のネストしたクラスおよびインタフェースすべてへのシンボリック参照が含まれている必要があります。

    すべてのインタフェースに、すべてのメンバー型(9.5)へのシンボリック参照、およびデフォルトのメソッドとフィールド・イニシャライザ内に出現するローカル・クラスと無名クラスへのシンボリック参照が含まれている必要があります。

  11. Javaコンパイラによって発行されたコンストラクトは、発行されたコンストラクトがクラス初期化メソッドでないかぎり、ソース・コードで明示的または暗黙的に宣言されたコンストラクトに対応していない場合、合成としてマークする必要があります(JVMS §2.9)。

  12. Javaコンパイラによって発行されたコンストラクトは、ソース・コードで暗黙的に宣言された仮パラメータに対応している場合、必須としてマークする必要があります(8.8.18.8.98.9.315.9.5.1)。

次の仮パラメータは、ソース・コードで暗黙的に宣言されます。

参考までに、次のコンストラクタは、ソース・コードでは暗黙的に宣言されますが、classファイル内で必須としてマークできるのは仮パラメータのみであるため、必須としてはマークされません(JVMS §4.7.24)。

モジュール宣言に対応するclassファイルには、バイナリ名がmodule-infoであり、スーパークラス、スーパーインタフェース、フィールドおよびメソッドを持たないクラスのclassファイルのプロパティが必要です。また、このモジュールのバイナリ表現には次のものがすべて含まれている必要があります。

次の各セクションでは、既存のバイナリとの互換性を損なわずにクラスおよびインタフェース宣言に加えることができる変更について説明します。前述の翻訳要件に基づき、Java仮想マシンおよびそのclassファイル形式はこれらの変更をサポートしています。前述の要件に基づくクラス・ローダーによってclassファイルにマップし戻される圧縮または暗号化された表現など、他の有効なバイナリ形式もすべて、これらの変更を必然的にサポートします。

13.3 パッケージおよびモジュールの展開

新しいクラスまたはインタフェースで、無関係なクラスまたはインタフェースに以前に指定された名前を再利用しないことを前提として、新しい最上位クラスまたはインタフェースを、既存のバイナリとの互換性を損なわずにパッケージに追加できます。新しいクラスまたはインタフェースで、無関係なクラスまたはインタフェースに以前に指定された名前を再利用する場合は、同じクラス・ローダーで両方のクラスまたはインタフェースのバイナリをロードできないため、競合が発生する場合があります。

publicではなく、それぞれがpublicクラスまたはインタフェースのスーパークラスまたはスーパーインタフェースでもない最上位クラスおよびインタフェース型クラスおよびインタフェースの変更は、それらが宣言されたパッケージ内のクラスおよびインタフェースにのみ影響します。そのようなクラスおよびインタフェースは、そのパッケージの影響を受けるバイナリが一緒に更新される場合は、ここでそれ以外に非互換性が説明されている場合でも、削除することも変更することもできます。

パッケージをエクスポートまたは開くために宣言されたモジュールが、パッケージをエクスポートしないか開かないように変更される場合、あるいはフレンドの異なるセットにパッケージをエクスポートまたは開くように変更される場合、パッケージのpublicおよびprotectedクラスおよびインタフェースへのアクセスを必要とするが、アクセスできなくなった既存のバイナリがリンクされると、IllegalAccessErrorがスローされます。広範に配布されたモジュールの場合、このような変更を行わないことをお薦めします。

特定のパッケージをエクスポートまたは開くようにモジュールが宣言されていない場合、パッケージをエクスポートまたは開くようにモジュールを変更しても、既存のバイナリとの互換性が損なわれることはありません。ただし、パッケージをエクスポートするようにモジュールを変更すると、そのモジュールを読み取るモジュールによって、同じ名前のパッケージをエクスポートする他のなんらかのモジュールも読み取られることがあるため、プログラムが開始されなくなる場合があります。

モジュール宣言にrequiresディレクティブを追加したり、requiresディレクティブにtransitive修飾子を追加しても、既存のバイナリとの互換性が損なわれることはありません。ただしこの場合は、モジュールが、同じ名前のパッケージをエクスポートする複数のモジュールを読み取るようになることがあるため、プログラムが開始されなくなる場合があります。

モジュール宣言のrequiresディレクティブを削除したり、requiresディレクティブからtransitive修飾子を削除すると、特定のモジュールによってエクスポートされるクラスおよびインタフェースを参照する過程での、そのモジュールの読取り可能性について、そのディレクティブまたは修飾子に依存していた既存のバイナリとの互換性が損なわれる可能性があります。既存のバイナリからのそのような参照がリンクされると、IllegalAccessErrorがスローされる可能性があります。

モジュール宣言でusesまたはprovidesディレクティブを追加または削除しても、既存のバイナリとの互換性が損なわれることはありません。

13.4 クラスの展開

13.4.4 スーパークラスおよびスーパーインタフェース

クラスがそれ自体のスーパークラスになると、ロード時にClassCircularityErrorがスローされます。既存のバイナリを使用して新しくコンパイルされたバイナリがロードされるときにこのような循環性の原因となるクラス階層への変更を、広範に配布されるクラスに対して行うことはお薦めしません。

クラスのスーパークラスまたはスーパーインタフェースそれぞれの合計セットがメンバーを失わないことを前提として、クラスの直接スーパークラスまたは直接スーパーインタフェーススーパーインタフェース型のセットを変更しても、既存のバイナリとの互換性が失われることはありません。

1つの注目すべき例を示すと、クラスのRAWスーパータイプを、RAW型によって名前が付けられるクラスまたはインタフェースのパラメータ化に置換することは、バイナリ互換性のある変更です。

直接スーパークラスまたは直接スーパーインタフェースのセットを変更した結果、クラスまたはインタフェースがそれぞれスーパークラスまたはスーパーインタフェースではなくなったときに、変更されたクラスのバイナリとともに既存のバイナリがロードされた場合、リンク・エラーが発生する可能性があります。広範に配布されるクラスの場合、このような変更を行わないことをお薦めします。

例13.4.4-1.スーパークラスの変更

次のテスト・プログラムが存在するとします。

class Hyper { char h = 'h'; } 
class Super extends Hyper { char s = 's'; }
class Test extends Super {
    public static void printH(Hyper h) {
        System.out.println(h.h);
    }
    public static void main(String[] args) {
        printH(new Super());
    }
}

これをコンパイルして実行すると、次の出力が得られます。

h

次に、クラスSuperの新しいバージョンをコンパイルするとします。

class Super { char s = 's'; }

クラスSuperのこのバージョンはHyperのサブクラスではありません。次に、Superの新しいバージョンを使用してHyperおよびTestの既存のバイナリを実行すると、リンク時にVerifyErrorがスローされます。このようにベリファイアが異議を申し立てるのは、SuperHyperのサブクラスではないため、new Super()の結果を型Hyperの仮パラメータのかわりに引数として渡すことができないからです。

検証ステップがなければどのようになる可能性があるかについて検討することをお薦めします。この場合、プログラムを実行すると、次のように出力される可能性があります。

s

これは、ベリファイアがなければ、互換性のないバイナリ・ファイルをリンクすると、これらの各ファイルが正しいJavaコンパイラによって生成されたものであったとしても、Java型システムが無効になる可能性があることを示しています。

ここから得られる教訓は、ベリファイアがない実装やベリファイアを使用できない実装の場合、型の安全性が維持されないため、有効な実装ではないということです。

マルチcatch句(14.20)の選択肢が相互のサブクラスまたはスーパークラスであってはならないという要件は、ソースの制限にすぎません。次のクライアント・コードが正当であるとします。

try {
    throwAorB();
} catch(ExceptionA | ExceptionB e) {
    ...
}

ここで、クライアントのコンパイル時にExceptionAExceptionBがサブクラス/スーパークラス関係を持たない場合、クライアントの実行時にExceptionAExceptionBのクライアントがこのような関係を持つことに関してはバイナリ互換性があります。

このことは、クライアントに対してバイナリ互換性があるクラス変換が同じクライアントに対してソース互換性がないような他の状況に類似しています。

13.4.8 フィールド宣言

広範に配布されるプログラムがフィールドをそのクライアントに公開しないようにしてください。これは、前述のバイナリ互換性の問題とは別の、一般的に推奨されるソフトウェア・エンジニアリングの慣例です。クラスにフィールドを追加すると、再コンパイルされていない既存のバイナリとの互換性が損なわれる可能性があります。

修飾TクラスCを持つフィールドfへの参照があるとします。さらに、fは実際には、TCのスーパークラスSで宣言されたインスタンス(それぞれstatic)フィールドであり、fの型はXであるとします。

fと同じ名前の型Xの新しいフィールドが、TCのスーパークラスSのサブクラスまたはTC自体に追加される場合、リンク・エラーが発生する可能性があります。そのようなリンク・エラーは、前述以外では、次のいずれかが当てはまる場合のみ発生します。

特に、フィールド・アクセスが、互換性のない型を持つスーパークラスのフィールドを以前に参照していたため、クラスを再コンパイルできなくなった場合は、リンク・エラーは発生しません。そのような参照がある、以前にコンパイルされたクラスは、引き続きスーパークラスで宣言されたフィールドを参照します。

...

13.4.12 メソッドおよびコンストラクタ宣言

メソッドまたはコンストラクタ宣言をクラスに追加した場合は、呼出しが、互換性のない型を持つスーパークラスのメソッドまたはコンストラクタを以前に参照していたため、クラスを再コンパイルできなくなった場合でも、既存のバイナリとの互換性が損なわれることはありません。そのような参照がある、以前にコンパイルされたクラスは、引き続きスーパークラスで宣言されたメソッドまたはコンストラクタを参照します。

修飾TクラスCを持つメソッドmへの参照があるとします。さらに、mは実際には、TCのスーパークラスSで宣言されたインスタンス(それぞれstatic)メソッドであるとします。

mと同じシグネチャおよび戻り型を持つ型Xの新しいメソッドが、TCのスーパークラスSのサブクラスまたはTC自体に追加される場合、リンク・エラーが発生する可能性があります。そのようなリンク・エラーは、前述以外では、次のいずれかが当てはまる場合のみ発生します。

...

13.4.26 enum enumクラスの展開

enum宣言の定数を追加したり、順序変更しても、既存のバイナリとの互換性が損なわれることはありません。

存在しなくなったenum定数に既存のバイナリがアクセスしようとすると、クライアントは実行時に失敗し、NoSuchFieldErrorが発生します。したがって、広範に配布されるenumについては、このような変更はお薦めしません。

enum定数を削除すると、対応する暗黙的なフィールド宣言が削除され、13.4.8に記載されているような結果になります。

他のあらゆる点において、enum enumクラスに関するバイナリ互換性のルールは、クラス標準クラスに関するものと同じです。

13.5 インタフェースの展開

13.5.7 注釈インタフェースの展開

注釈インタフェースは、他のインタフェースとまったく同じように動作します。注釈インタフェースに対する要素の追加または削除は、メソッドの追加または削除に似ています。注釈インタフェースを繰返し可能にするなど(9.6.3)、注釈インタフェースに対する他の変更を左右する重要な考慮事項がありますが、これらは、Java仮想マシンによるバイナリのリンケージには影響しません。かわりに、こうした変更は、注釈を操作する反射型APIの動作に影響を及ぼします。これらのAPIのドキュメントには、基になる注釈インタフェースに対して様々な変更が加えられたときのそれらの動作が規定されています。

注釈を追加または削除しても、Javaプログラミング言語によるプログラムのバイナリ表現の正しいリンケージには影響しません。

第15章: 式

15.8 Primary式

15.8.2 クラス・リテラル

クラス・リテラルは、クラス、インタフェース、配列またはプリミティブ型の名前、あるいは疑似型voidと、その後に「.」とトークンclassが続くように構成された式です。

ClassLiteral:
TypeName {[ ]} . class
NumericType {[ ]} . class
boolean {[ ]} . class
void . class

TypeNameは、アクセス可能(6.6)であるクラスまたはインタフェース示す必要があります。TypeNameが、アクセス不可能であるクラスまたはインタフェース、または型変数を示す場合、コンパイル時にエラーが発生します。

Cがクラス、インタフェースまたは配列型(4.3)の名前であるC.classの型はClass<C>です。

pがプリミティブ型(4.2)の名前であるp.classの型はClass<B>です。この場合、Bはボックス化変換([5.1.7])の後の型pの式の型です。

void.class (8.4.5)の型はClass<Void>です。

クラス・リテラルは、現在のインスタンスのクラスの定義側のクラス・ローダー(12.2)によって定義されている名前付きクラス、インタフェース、配列型またはプリミティブ型(またはvoid)のClassオブジェクトに評価されます。

15.8.3 this

キーワードthisは次のコンテキストでのみ使用できます。

他の場所に出現する場合は、コンパイル時にエラーが発生します。

キーワードthisは、ラムダ式が出現するコンテキストで許可される場合のみ、ラムダ式に使用できます。そうでない場合、コンパイル時にエラーが発生します。

1次式として使用する場合、キーワードthisはインスタンス・メソッドまたはデフォルト・メソッドを呼び出すオブジェクト(15.12)、または構成されるオブジェクトへの参照である値を示します。ラムダ本体でthisが示す値は、それを囲むコンテキストのthisが示す値と同じものです。

キーワードthisは明示的なコンストラクタ呼出し文でも使用されます(8.8.7.1)。

thisの型は、中にキーワードthisが出現するクラスまたはインタフェース型Tです。Cは、this式の最も内側のクラスまたはインタフェース宣言であるとします。Cが汎用であり、型パラメータF1, ..., Fnを持つ場合、thisの型はC<F1, ..., Fn>です。それ以外の場合、thisの型はCです。

デフォルト・メソッドは、インタフェース内でthisにアクセスする独自の機能を提供します。(その他のインタフェース・メソッドはすべてabstractまたはstaticであるため、thisにはアクセスできません。)その結果、thisにインタフェース型を持たせることができます。

現在、これは、デフォルトのメソッドで、かつprivateメソッドです。ただし、デフォルトのメソッドが導入されたときは、このコメントは興味深いものでした。現在、これは単に、インタフェースは本体を持つインスタンス・メソッドを持つことができるという、明白なことを言い換えたものです。

TCがクラスであるか、TのサブタイプであるクラスCのサブクラスである場合、実行時に、参照される実際のオブジェクトのクラスは、TCになる場合があります。

例15.8.3-1.this

class IntVector {
    int[] v;
    boolean equals(IntVector other) {
        if (this == other)
            return true;
        if (v.length != other.v.length)
            return false;
        for (int i = 0; i < v.length; i++) {
            if (v[i] != other.v[i]) return false;
        }
        return true;
    }
}

ここでは、クラスIntVectorは2つのベクターを比較するメソッドequalsを実装します。一方のベクターが、equalsメソッドが呼び出されたものと同じベクター・オブジェクトの場合、長さと値の比較チェックをスキップできます。equalsメソッドでは、もう一方のオブジェクトへの参照をthisと比較してこのチェックを実装します。

15.8.4 修飾されたthis

字句的な包含インスタンス(8.1.3)は、キーワードthisを明示的に修飾することによって参照できます。

Tは、TypeNameが示す型であるとします。nは、TTypeNameが、修飾されたthis式が出現するクラスまたはインタフェースのn番目の字句的な包含クラスまたはインタフェース宣言であるような整数であるとします。

TypeName.thisという形式の式の値は、thisn番目の字句的な包含インスタンスです。

TypeNameが汎用であり、型パラメータF1, ..., Fnを持つ場合、式の型はTypeName<F1, ..., Fn>です。それ以外の場合、式の型はCTypeNameです。

TypeNameが式の字句的な包含クラスまたはインタフェース宣言ではない場合、あるいはクラスTTypeNameまたはTTypeName自体の内部クラスではないクラスまたはインタフェースに式が出現する場合、コンパイル時にエラーが発生します。

15.9 クラス・インスタンス作成式

15.9.1 インスタンス化対象のクラスの確認

ClassOrInterfaceTypeToInstantiateが(<>ではなく)TypeArgumentsで終わる場合、ClassOrInterfaceTypeToInstantiateが整形式のパラメータ化された型(4.5)を表す必要があります。そうでない場合、コンパイル時にエラーが発生します。

ClassOrInterfaceTypeToInstantiate<>で終わるが、ClassOrInterfaceTypeToInstantiate内のIdentifierによって表されるクラスまたはインタフェースが汎用ではない場合、コンパイル時にエラーが発生します。

クラス・インスタンス作成式がクラス本体で終わる場合、インスタンス化対象のクラスは匿名クラスです。次に、

クラス・インスタンス作成式が匿名クラスを宣言しない場合、次のようになります。

15.9.2 包含インスタンスの特定

Cはインスタンス化されるクラス、iは作成されるインスタンスであるとします。Cが内部クラスである場合、iには、直接包含するインスタンス(8.1.3)がある可能性があり、次のように特定されます。

Cが無名クラスで、かつその直接スーパークラスSが内部クラスである場合、iは、Sに関して直接包含するインスタンスを持つ可能性があり、次のように判別されます。

15.9.3 コンストラクタとその引数の選択

Cは、インスタンス化対象のクラスであるとします。Cのインスタンスiを作成する場合は、次のルールによって、コンパイル時にCのコンストラクタが選択されます。

まず、コンストラクタ呼出しの実際の引数が決定します。

次に、Cのコンストラクタと、対応するthrows句および戻り型が決定します。

クラス・インスタンス作成式の引数が、呼出しタイプ(15.12.2.6)から導出されたそのターゲット型と互換性がない場合は、コンパイル時にエラーが発生します。

コンパイル時宣言が可変引数呼出し(15.12.2.4),によって適用可能である場合、コンストラクタの呼出しタイプの最後の仮パラメータ型がFn[]であるとすると、Fnのイレイジャである型が呼出しの時点でアクセス可能でない場合、コンパイル時にエラーが発生します。

クラス・インスタンス作成式の型は、前述の定義のとおり、選択されたコンストラクタに対応する戻り型です。

15.9.4 クラス・インスタンス作成式の実行時評価

実行時の、クラス・インスタンス作成式の評価は、次のようになります。

まず、クラス・インスタンス作成式が、修飾されたクラス・インスタンス作成式である場合、修飾する1次式が評価されます。修飾する式がnullと評価されると、NullPointerExceptionが発生し、クラス・インスタンス作成式は突然完了します。修飾する式が突然完了する場合、クラス・インスタンス作成式が同じ理由で突然完了します。

次に、新しいクラス・インスタンスに領域が割り当てられます。オブジェクトを割り当てるための領域が不十分である場合、OutOfMemoryErrorがスローされて、クラス・インスタンス作成式の評価が突然完了します。

新しいオブジェクトには、指定されたクラスとそのすべてのスーパークラスで宣言されたすべてのフィールドの新しいインスタンスが含まれます。それぞれの新しいフィールド・インスタンスが作成されたら、そのデフォルト値に初期化されます(4.12.5)。

次に、コンストラクタの実際の引数が左から右に評価されます。いずれかの引数の評価が突然完了する場合、その右の引数式は評価されず、クラス・インスタンス作成式が同じ理由で突然完了します。

次に、指定されたクラスの選択されたコンストラクタが呼び出されます。これにより、クラスのスーパークラスごとに1つ以上のコンストラクタが呼び出されます。このプロセスは、明示的なコンストラクタ呼出し文(8.8.7.1)によって指示でき、詳細が12.5に規定されています。

クラス・インスタンス作成式の値は、指定されたクラスの新しく作成されたオブジェクトへの参照です。式が評価されるたびに、新しいオブジェクトが作成されます。

...

15.9.5 匿名クラス宣言

Javaコンパイラにより、クラス・インスタンス作成式、またはクラス本体で終わるenum定数(8.9.1)から匿名クラス宣言が自動的に導出されますによって暗黙的に匿名クラスが宣言されます

匿名クラスは決してabstract (8.1.1.1)ではありません。

クラス・インスタンス作成式によって宣言される匿名クラスは、決してfinal (8.1.1.2)ではありません。enum定数によって宣言される匿名クラスは、常にfinalです。

これらのルールをより妥当なものにするための長い取り組みの歴史がありますが、互換性の問題によって断念されたと考えられます。前述の内容はJDK 14のjavacの実際の動作に反映されます。

匿名クラスがfinalではないという事実finalの処理は、キャスト、特にキャスト演算子(5.5)で許可された絞り込み参照変換に関連しています。これは、匿名クラスが非finalであっても、extends句(8.1.4)によって匿名クラスに名前を付けることができないため、匿名クラスのサブクラスを宣言することが不可能であるという点でため、サブクラス化にも関連しますには関連しません

匿名クラスは常に内部クラス(8.1.3)です。決してstatic (8.1.18.5.1)ではありません。

static修飾子は、メンバー・クラスに対してのみ意味があります。アクセス制御に言及がないことと同様に、ここでこれについて言及する必要はありません。

クラス・インスタンス作成式によって宣言された匿名クラスの直接スーパークラスまたは直接スーパーインタフェースは、クラス・インスタンス作成式(15.9.1)によって与えられます。この場合、コンストラクタ(15.9.3)の選択時に型引数が必要に応じて推測されます。直接スーパーインタフェース型が指定されている場合、直接スーパークラス型はObjectです。

enum定数によって宣言される匿名クラスの直接スーパークラス型は、宣言するenumクラスの型です。

クラス・インスタンス作成式またはenum定数のClassBodyにより、匿名クラスのフィールド(8.3)、メソッド(8.4)、メンバー・クラス(8.5)およびインスタンス・イニシャライザ(8.6)が宣言されます。匿名クラスのコンストラクタは常に暗黙的です(15.9.5.1)。

クラス・インスタンス作成式に<>が匿名クラスとともに使用される場合、匿名クラス本体内で宣言されたすべての非privateメソッドについて、メソッド宣言に@Override (9.6.4.4)が注釈として付けられたかのようになります。

<>が使用される場合、推測された型引数はプログラマによって予期されたとおりにはならない可能性があります。この結果、匿名クラスのスーパータイプが予期されたとおりにはならない可能性があり、匿名クラスで宣言されたメソッドが意図したとおりにスーパータイプ・メソッドをオーバーライドしない可能性があります。このようなメソッドを@Overrideが注釈として付けられたかのように扱うと(これらに@Overrideが明示的に注釈として付けられていない場合)、気付かれずに間違った状態になっているプログラムを回避しやすくなります。

15.9.5.1 無名コンストラクタ

匿名クラスは、明示的に宣言されたコンストラクタを持つことができません。かわりに、無名コンストラクタが、匿名クラスに対して暗黙的に宣言されます。直接スーパークラスSAを持つ匿名クラスCの無名コンストラクタの形式は、次のようになります。

15.9.3では、Sを使用して、スーパークラス型について説明しています。ここでは、その型によって名前が付けられるクラスについて説明します。異なる文字Aを使用して、2つを区別すると便利です。

すべての場合に、無名コンストラクタのthrows句は、15.9.3に規定されているように、無名コンストラクタ内に含まれる明示的なスーパークラス・コンストラクタ呼出し文によってスローされるすべてのチェック例外と、匿名クラスのインスタンス・イニシャライザまたはインスタンス変数イニシャライザによってスローされるすべてのチェック例外をリストする必要がありますリストします

無名コンストラクタのシグネチャは、アクセス不可能な型を参照できます(たとえば、そのような型がスーパークラス・コンストラクタcsのシグネチャに出現した場合など)。これ自体では、コンパイル時にも実行時にもエラーが発生することはありません。

15.12 メソッド呼出し式

15.12.1 コンパイル時のステップ1: 検索するクラスまたはインタフェースの特定

コンパイル時にメソッド呼出しを処理するときの最初のステップは、呼び出されるメソッドの名前と、その名前のメソッドの定義を検索するクラスまたはインタフェースを特定することです。

メソッドの名前は、MethodInvocationの左カッコの直前にあるMethodNameまたはIdentifierによって指定されます。

検索するクラスまたはインタフェースについては、MethodInvocationの左カッコの前にある形式に応じて、次の6つのケースを検討します。

TypeName . super構文はオーバーロードされます。呼出しが字句的な包含クラス宣言内の修飾されていないsuperである場合のように、従来、TypeNameは、クラスである字句的な包含クラス宣言を参照し、ターゲットはこのクラスのスーパークラスです。

class Superclass {
    void foo() { System.out.println("Hi"); }
}

class Subclass1 extends Superclass {
    void foo() { throw new UnsupportedOperationException(); }

    Runnable tweak = new Runnable() {
        void run() {
            Subclass1.super.foo();  // Gets the 'println' behavior
        }
    };
}

スーパーインタフェースのデフォルト・メソッドの呼出しをサポートするために、TypeNameは、現在のクラスまたはインタフェースの直接スーパーインタフェースを参照することもできます。ターゲットはそのスーパーインタフェースです。

interface Superinterface {
    default void foo() { System.out.println("Hi"); }
}

class Subclass2 implements Superinterface {
    void foo() { throw new UnsupportedOperationException(); }

    void tweak() {
        Superinterface.super.foo();  // Gets the 'println' behavior
    }
}

これらの形式の組合せ、つまり、呼出しの形式が字句的な包含クラス宣言内のInterfaceName . superである場合のように、クラスである字句的な包含クラス宣言のスーパーインタフェース・メソッドを呼び出すことをサポートする構文はありません。

class Subclass3 implements Superinterface {
    void foo() { throw new UnsupportedOperationException(); }

    Runnable tweak = new Runnable() {
        void run() {
            Subclass3.Superinterface.super.foo();  // Illegal
        }
    };
}

次善策は、インタフェースのsuperコールを実行する字句的な包含クラス宣言でprivateメソッドを導入することです。

15.12.2 コンパイル時のステップ2: メソッド・シグネチャの特定

15.12.2.1 適用可能なメソッドの特定

このメソッド呼出しに適用可能なすべてのメンバー・メソッドを対象に、コンパイル時のステップ1 (15.12.1)で特定されたクラスまたはインタフェースが検索されます。スーパークラスおよびスーパーインタフェースから継承されるメンバーは、この検索に含まれます。

...

15.12.3 コンパイル時のステップ3: 選択したメソッドが適切かどうか

メソッド呼出しについて最も的確なメソッド宣言がある場合、メソッド呼出しのコンパイル時宣言と呼ばれます。

メソッド呼出しの引数が、コンパイル時宣言の呼出しタイプから導出されたそのターゲット型と互換性がない場合は、コンパイル時にエラーが発生します。

コンパイル時宣言が可変引数呼出しによって適用可能である場合、メソッドの呼出しタイプの最後の仮パラメータ型がFn[]であるとすると、Fnのイレイジャである型が呼出しの時点でアクセス可能でない場合(6.6)、コンパイル時にエラーが発生します。

コンパイル時宣言がvoidである場合、メソッド呼出しは、最上位式(つまり、式の文にあるか、for文のForInitまたはForUpdate部分にあるExpression)である必要があります。そうでない場合はコンパイル時にエラーが発生します。このようなメソッド呼出しは値を生成しないため、値が不要な状況でのみ使用する必要があります。

さらに、コンパイル時宣言が適切であるかどうかは、次のように、左カッコの前にあるメソッド呼出し式の形式によって異なることがあります。

...

15.12.4.3 型およびメソッドのアクセス可能性のチェック

このセクションでは:

Javaプログラミング言語の実装により、リンケージの一部として、TクラスまたはインタフェースQがアクセス可能になる必要があります。

TQprotectedである場合、これは必然的にネストされたクラスまたはインタフェースになるため、コンパイル時に、そのアクセス可能性は、その宣言を包含するクラスまたはインタフェースのアクセス可能性の影響を受けます。ただし、リンケージ中は、そのアクセス可能性は、その宣言を包含するクラスまたはインタフェースのアクセス可能性の影響を受けません。さらにリンケージ中は、protectedTQは、publicTQとしてアクセス可能です。このようなコンパイル時のアクセス制御(6.6)と実行時のアクセス制御の相違の原因は、Java仮想マシンの制限です。

実装により、リンケージ中に、メソッドmが引き続きTQあるいはTQスーパータイプスーパークラスまたはスーパーインタフェースに見つかるようになる必要もあります。mが見つからない場合、NoSuchMethodError (IncompatibleClassChangeErrorのサブクラス)が発生します。mが見つかる場合、Cは、mを宣言するクラスまたはインタフェースであるとします。実装により、リンケージ中に、Cmの宣言がDにアクセスできるようになる必要があります。

TQmのいずれかがアクセス不可能である場合、IllegalAccessErrorが発生します(12.3)。

呼出しモードがinterfaceの場合、実装によって、ターゲット参照クラスが、指定されたインタフェースを引き続き実装することをチェックする必要があります。ターゲット参照クラスがインタフェースを引き続き実装しない場合、IncompatibleClassChangeErrorが発生します。

15.12.4.4 呼び出すメソッドの特定

前のセクション(15.12.4.3)と同様に、次のようになります。

呼び出すメソッドを特定するための戦略は、呼出しモードに応じて異なります。

...

15.13 メソッド参照式

呼出しを実際に実行することなく、メソッドの呼出しを参照するために、メソッド参照式が使用されます。特定の形式のメソッド参照式でも、メソッド呼出しと同様に、クラス・インスタンスの作成(15.9)または配列の作成(15.10)を処理できます。

MethodReference:
ExpressionName :: [TypeArguments] Identifier
Primary :: [TypeArguments] Identifier
ReferenceType :: [TypeArguments] Identifier
super :: [TypeArguments] Identifier
TypeName . super :: [TypeArguments] Identifier
ClassType :: [TypeArguments] new
ArrayType :: new

::の右側にTypeArgumentsが存在する場合、いずれかの型引数がワイルドカード(4.5.1)であると、コンパイル時にエラーが発生します。

メソッド参照式の形式がExpressionName :: [TypeArguments] IdentifierまたはPrimary :: [TypeArguments] Identifierの場合、ExpressionNameまたはPrimaryの型が参照型ではないと、コンパイル時にエラーが発生します。

メソッド参照式の形式がsuper :: [TypeArguments] Identifierの場合、TEは、メソッド参照式を直接包含するクラスまたはインタフェース宣言であるとします。TEがクラスObjectであるか、TEがインタフェースである場合は、コンパイル時にエラーが発生します。

メソッド参照式の形式がTypeName . super :: [TypeArguments] Identifierの場合、次のようになります。

メソッド参照式の形式がsuper :: [TypeArguments] IdentifierまたはTypeName . super :: [TypeArguments] Identifierの場合、式が静的コンテキストに出現するとコンパイル時にエラーが発生します。

メソッド参照式の形式がClassType :: [TypeArguments] newの場合、次のようになります。

メソッド参照式の形式がArrayType :: newの場合、ArrayTypeは、具象化可能(4.7)である型を示している必要があります。そうでない場合、コンパイル時にエラーが発生します。

...