このドキュメントでは、クラスおよびインタフェースに関連する用語とタクソノミの使用方法を明確にして、クラスおよびインタフェースと型をよりはっきりと区別するための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]
[SuperclassClassExtends] [SuperinterfacesClassImplements] ClassBody非ターミナルのNormalClassDeclarationの1つの右側が定義されます。
8.1の変更を反映します。
...
第4章: 型、値および変数
4.5 パラメータ化された型
汎用のクラスまたはインタフェース宣言(8.1.2、9.1.2)では、パラメータ化された型のセットを定義します。
パラメータ化された型は、形式C<
T1,...,Tn>
のクラスまたはインタフェース型です。この場合、Cは汎用型クラスまたはインタフェースの名前で、<
T1,...,Tn>
は汎用型クラスまたはインタフェースの特定のパラメータ化を示す型引数のリストです。
汎用型クラスまたはインタフェースは、型パラメータF1,...,Fnと、対応する境界B1,...,Bnを持ちます。パラメータ化された型のそれぞれの型引数Tiの対象は、対応する境界にリストされたすべての型のサブタイプであるすべての型です。つまり、Biのそれぞれの境界型Sについては、TiがS[F1:=T1,...,Fn:=Tn]
のサブタイプになります(4.10)。
次がすべて当てはまる場合、パラメータ化された型C<
T1,...,Tn>
は整形式です。
Cが、汎用
型クラスまたはインタフェースの名前である。型引数の数が、Cの汎用宣言の型パラメータの数と同じである。
型C
<
X1,...,Xn>
になるキャプチャ変換(5.1.10)の対象となる場合に、Biのそれぞれの境界型Sについて、各型引数XiがS[F1:=X1,...,Fn:=Xn]
のサブタイプである。
パラメータ化された型が整形式ではない場合は、コンパイル時にエラーが発生します。
この仕様では、クラスまたはインタフェース型に言及する場合は、常に、明示的に除外されていないかぎり、汎用バージョンパラメータ化された型も含まれます。
次のいずれかが当てはまる場合、2つのパラメータ化された型は明確に異なります。
これらが、別個の汎用型宣言のパラメータ化である。
その型引数のいずれかが明確に異なる。
8.1.2の例の汎用
型クラスを考慮した場合の、整形式のパラメータ化された型を次にいくつか示します。
Seq<String>
Seq<Seq<String>>
Seq<String>.Zipper<Integer>
Pair<String,Integer>
これらの汎用
型クラスの間違ったパラメータ化を次にいくつか示します。
Seq<int>
は、プリミティブ型を型引数にすることはできないため、不正です。
Pair<String>
は、十分な型引数がないため、不正です。
Pair<String,String,String>
は、型引数が多すぎるため、不正です。
パラメータ化された型は、
ネストされた汎用クラスまたはインタフェースのパラメータ化になる場合があります。たとえば、非汎用クラス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は(ワイルドカードではなく)型です。次に、
mは、宣言される型がTである、Cのメンバーまたはコンストラクタ宣言であるとします(8.2、8.8.6)。
C
<
T1,...,Tn>
におけるmの型は、T[A1:=T1,...,An:=Tn]
です。mは、Dのメンバーまたはコンストラクタ宣言であるとします。この場合、Dは、Cによって拡張されるクラス、またはCによって実装されるインタフェースです。D
<
U1,...,Uk>
は、Dに対応するC<
T1,...,Tn>
のスーパータイプ(4.10.2)であるとします。C
<
T1,...,Tn>
におけるmの型は、D<
U1,...,Uk>
におけるmの型です。
Cのパラメータ化の型引数のいずれかがワイルドカードである場合、次のようになります。
C
<
T1,...,Tn>
におけるフィールド、メソッドおよびコンストラクタの型は、C<
T1,...,Tn>
のキャプチャ変換のフィールド、メソッドおよびコンストラクタの型です(5.1.10)。Dは、Cの(場合によっては汎用の)クラスまたはインタフェース宣言であるとします。この場合、C
<
T1,...,Tn>
におけるDの型はDです。ここで、Dが汎用の場合、すべての型引数は制限のないワイルドカードです。
パラメータ化された型のメンバーにアクセスするにはキャプチャ変換を実行する必要があり、かつ、クラス・インスタンス作成式(15.9)のキーワード
new
の後にはワイルドカードを使用できないため、このことはまったく重大ではありません。
前の段落の唯一の例外は、ネストされたパラメータ化された型が
instanceof
演算子(15.20.2)の式として使用される場合となり、この場合、キャプチャ変換は適用されません。
汎用型宣言クラスまたはインタフェースで宣言されるstatic
メンバーは、汎用型に対応する非汎用型汎用クラスまたはインタフェースの名前を使用して参照される必要があります(6.1、6.5.5.2、6.5.6.2)。そうしないと、コンパイル時にエラーが発生します。
つまり、パラメータ化された型を使用して、汎用型宣言で宣言された
static
メンバーを参照することは不正になります。
4.8 RAW型
非汎用レガシー・コードとの連携を促進するために、パラメータ化された型(4.5)のイレイジャ(4.6)、または要素型がパラメータ化された型である配列型(10.1)のイレイジャを型として使用できます。そのような型はRAW型と呼ばれます。
より正確に言うと、RAW型は次のいずれかとして定義できます。
型引数リストを伴わずに汎用
型クラスまたはインタフェース宣言の名前を付けることで形成される参照型。要素型がRAW型である配列型。
Rのスーパークラスまたはスーパーインタフェースから継承されていないRAW型Rの
非内部メンバー・クラスの名前。static
メンバー型
非汎用クラスまたはインタフェース型の型はRAW型ではありません。
RAW型の
非内部メンバー・クラスの名前がRAWとみなされる理由を確認するために、次の例を考えてみます。static
メンバー型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.4、9.4)または非static
フィールド(8.3)の型は、汎用クラスまたはインタフェースCに対応する汎用宣言におけるその型のイレイジャです。
メンバーがクラスまたはインタフェースDで宣言された、RAW型Cの継承されるインスタンス・メソッドまたは非static
フィールドの型は、名前がDのCのスーパータイプのメンバーの型です。
クラスの継承は8.4.8で定義されていますが、RAW型を介してアクセスする場合の、継承されたメンバーの型について説明する必要があります。RAW型のスーパータイプが消去されると表現するのみでは十分ではありません。
RAW型Cのstatic
メソッドまたはstatic
フィールドの型は、汎用クラスまたはインタフェースCに対応する汎用宣言におけるその型と同じになります。
そのスーパークラスまたはスーパーインタフェースから継承されないRAW型の非static
型メンバー・クラスまたはインタフェースに型引数を渡すと、コンパイル時にエラーが発生します。
パラメータ化された型の型メンバー・クラスまたはインタフェースをRAW型として使用しようとすると、コンパイル時にエラーが発生します。
これは、「未加工の」型についての禁止事項が、修飾型がパラメータ化されるが内部クラスをRAW型として使用しようとする場合にまで及ぶことを意味します。
Outer<Integer>.Inner x = null; // illegal
これは、前述のものとは反対のケースです。この不完全な型が妥当であることを示す実質的な根拠はありません。レガシー・コードでは、型引数は使用されません。非レガシー・コードでは、汎用型を適切に使用して、すべての必要な型引数を渡す必要があります。
クラスのスーパータイプはRAW型になる場合があります。このクラスのメンバー・アクセスは通常どおり扱われ、このスーパータイプのメンバー・アクセスはRAW型の場合と同様に扱われます。クラスのコンストラクタでは、super
への呼出しは、RAW型でのメソッド呼出しとして扱われます。
RAW型の使用は、レガシー・コードの互換性の便宜的措置としてのみ許可されています。Javaプログラミング言語に汎用が導入された後に作成するコードでRAW型を使用することはお薦めしません。今後のバージョンのJavaプログラミング言語でRAW型を使用できなくなる可能性があります。
型指定ルールの潜在的な違反が常に確実に通知されるように、RAW型のメンバーがアクセスされると、特定の場合に、コンパイル時に未チェック警告が発生します。RAW型のメンバーまたはコンストラクタにアクセスしたときのコンパイル時の未チェック警告に関するルールは、次のとおりです。
フィールドへの割当て時: フィールド・アクセス式(15.11)のPrimaryの型がRAW型の場合、イレイジャによってフィールドの型が変更されると、コンパイル時に未チェック警告が発生します。
メソッドまたはコンストラクタの呼出し時: 検索対象のクラスまたはインタフェースの型(15.12.1)がRAW型の場合、イレイジャによってメソッドまたはコンストラクタの仮パラメータ型のいずれかが変更されると、コンパイル時に未チェック警告が発生します。
イレイジャによって仮パラメータ型が変更されない場合(戻り型または
throws
句(あるいはその両方)が変更される場合も同様)のメソッド呼出し、フィールドからの読取り、および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は、次のように、交差型のメンバーを識別する目的で概念的なクラスまたはインタフェースを帰納します。
それぞれのTi (1 ≤ i ≤ n)については、Ti
<:
Ciになるように、Ciは、最も的確なクラスまたは配列型であるとします。このとき、i (1 ≤ i ≤ n)の場合はCk<:
CiになるようにCkがいくつか存在している必要があります。そうでない場合、コンパイル時にエラーが発生します。1 ≤ j ≤ nの場合にTjが型変数である場合は、Tj'は、Tjの
public
メンバーと同じメンバーを持つインタフェースであるとします。それ以外の場合にTjがインタフェースである場合は、Tj'はTjであるとします。Ckが
Object
である場合、概念的なインタフェースが帰納されます。それ以外の場合、直接スーパークラス型Ckによって概念的なクラスが帰納されます。このクラスまたはインタフェースは、直接スーパーインタフェーススーパーインタフェース型T1', ..., Tn'を持ち、交差型が出現するパッケージで宣言されます。
交差型のメンバーは、それによって帰納されるクラスまたはインタフェースのメンバーになります。
交差型と型変数の境界の違いは、深く検討する価値があります。すべての型変数の境界は、交差型を帰納します。この交差型は、一般的に、単一の型で構成される些細なものです。境界の形式は、特定の不自然な状況が発生しなくなるように制限(クラスまたは型変数にできるのは最初の要素のみ、かつ境界に出現できる型変数は1つのみ)されます。ただし、キャプチャ変換によって、配列型などのより一般的な境界を持つ型変数が作成される可能性があります。
4.10 サブタイプ
4.10.2 クラスおよびインタフェース型間のサブタイプ
非汎用型宣言クラスまたはインタフェースCの場合、Cの型の直接スーパータイプは、次のすべてです。
<
F1,...,Fn>
(n > 0)の型パラメータを持つ汎用型宣言クラスまたはインタフェースCの場合、RAW型C (4.8)の直接スーパータイプは、次のすべてです。
Cがクラスの場合、
RAW型Cの直接スーパークラス型のイレイジャ(4.6)。RAW型Cの直接スーパーインタフェーススーパーインタフェース型のイレイジャ。C
が直接<
F1,...,Fn>
スーパーインタフェース(9.1.2)スーパーインタフェース型を持たない汎用インタフェース型インタフェースの場合の型Object
。
汎用型宣言C<
F1,...,Fn>
(n > 0)の場合、汎用型C<
F1,...,Fn>
の直接スーパータイプは、次のすべてです。
C
<
F1,...,Fn>
の直接スーパークラス。C
<
F1,...,Fn>
の直接スーパーインタフェース。C
<
F1,...,Fn>
が直接スーパーインタフェースを持たない汎用インタフェース型の場合の型Object
。RAW型C。
<
F1,...,Fn>
(n > 0)の型パラメータを持つ汎用型宣言クラスまたはインタフェースCの場合、パラメータ化された型C<
T1,...,Tn>
(それぞれのTi (1 ≤ i ≤ n)は型)の直接スーパータイプは、次のすべてです。
D<
U1 θ,...,Uk θ>
。ここで、D<
U1,...,Uk>
は、汎用型C<
F1,...,Fn>
の直接スーパータイプである汎用型で、θは[F1:=T1,...,Fn:=Tn]
の置換です。Cがクラスの場合、Cの直接スーパークラス型に適用される
[F1:=T1,...,Fn:=Tn]
の置換。Cの直接スーパーインタフェース型に適用される
[F1:=T1,...,Fn:=Tn]
の置換。C
<
S1,...,Sn>
。ここで、SiにはTi (1 ≤ i ≤ n) (4.5.1)が含まれます。C
<F1,...,Fn>が直接スーパーインタフェーススーパーインタフェース型を持たない汎用インタフェース型インタフェースの場合の型Object
。RAW型C。
<
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の型コンテキストがあります。
宣言内:
式内:
明示的なコンストラクタ呼出し文、
またはクラス・インスタンス作成式、またはメソッド呼出し式またはメソッド参照式(8.8.7.1、15.9、15.12、15.13)に対する明示的な型引数リストの型インスタンス化対象のクラス型として(15.9)またはインスタンス化対象の匿名クラスの直接スーパークラス型または直接スーパーインタフェース型としての非修飾クラス・インスタンス作成式内(15.9.5)
配列作成式内の要素型(15.10.1)
キャスト式のキャスト演算子内の型(15.16)
instanceof
関係演算子に続く型(15.20.2)メンバー・メソッドを検索するための参照型として、またはコンストラクトに対するクラス型または配列型としてのメソッド参照式内(15.13)。
また、型は次としても使用されます。
前述の任意のコンテキスト内の配列型の要素型
前述の任意のコンテキスト内でパラメータ化された型の非ワイルドカード型引数またはワイルドカード型引数の境界。
最後に、Javaプログラミング言語には、型の使用方法を示す3つ2つの特別な語句があります。
制限のないワイルドカード(4.5.1)
配列型を示すための可変引数パラメータの型内の
...
(8.4.1)コンストラクタ・オブジェクトのクラスを示すためのコンストラクタ宣言(8.8)内の型の単純名コンストラクタ宣言で使用される識別子は、型ではなくクラス名です。
型コンテキストの型の意味は、次によって与えられます。
4.2: プリミティブ型の場合
4.4: 型パラメータの場合
4.5: パラメータ化されたクラスおよびインタフェース型、またはパラメータ化された型内の型引数として、またはパラメータ化された型内のワイルドカード型引数の境界として表示されるクラスおよびインタフェース型の場合
4.8: RAWであるクラスおよびインタフェース型の場合
4.9: 型パラメータの境界内の交差型の場合
6.5:
汎用性が重要ではないコンテキスト(6.1)の非汎用クラスおよびインタフェースのクラスおよびインタフェース型と型変数の場合10.1: 配列型の場合
一部の型コンテキストは、参照型をパラメータ化する方法を制限します。
次の型コンテキストでは、型がパラメータ化された参照型である場合、ワイルドカード型引数がないことが必須となります。
インタフェース宣言の
extends
句内(9.1.3)インスタンス化対象のクラス型として(15.9)またはインスタンス化対象の匿名クラスの直接スーパークラス型または直接スーパーインタフェース型としての非修飾クラス・インスタンス作成式内(15.9.5)
メンバー・メソッドを検索するための参照型として、またはコンストラクトに対するクラス型または配列型としてのメソッド参照式内(15.13)。
また、明示的なコンストラクタ呼出し文、クラス・インスタンス作成式、メソッド呼出し式、またはメソッド参照式に対する明示的な型引数リストでは、ワイルドカード型引数は許可されません(8.8.7.1、15.9、15.12、15.13)。
次の型コンテキストでは、型がパラメータ化された参照型である場合、制限のないワイルドカード型引数のみがある(つまり、具象化可能型である)ことが必須となります。
次の型コンテキストでは、パラメータ化された参照型には例外が含まれ、例外の型が非汎用であるため、これを一緒に使用することは許可されません(6.1)。
型が使用される任意の型コンテキストで、プリミティブ型を表すキーワード、または参照型の単純名を表すIdentifierに注釈を付けることができます。また、配列型内のネストの目的のレベルで
[
の左側に注釈を付けることにより、配列型に注釈を付けることもできます。これらの場所にある注釈は型注釈と呼ばれ、9.7.4に規定されています。いくつか例を挙げます。
@Foo int[] f;
は、プリミティブ型int
に注釈を付けます
int @Foo [] f;
は、配列型int[]
に注釈を付けます
int @Foo [][] f;
は、配列型int[][]
に注釈を付けます
int[] @Foo [] f;
は、配列型int[][]
のコンポーネント型である配列型int[]
に注釈を付けます
宣言内に現れる5つの型コンテキストは、宣言コンテキストと同じ数の構文上の場所を占有します(9.6.4.1)。
メソッドの戻り型(注釈
型インタフェースの要素の型を含む)クラスまたはインタフェースのフィールド宣言内の型(enum定数を含む)
メソッド、コンストラクタまたはラムダ式の仮パラメータ宣言内の型
ローカル変数制限内の型
例外パラメータ宣言内の型
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); }
}
この例では、型は次の宣言に使用されています。
インポートされた型(7.5): ここでは、パッケージjava.util
の型java.util.Random
からインポートされた型Random
が宣言されています型ではなく、クラスおよびインタフェースがインポートされます。
クラスのクラス変数およびインスタンス変数(8.3)、およびインタフェースの定数(9.3)であるフィールド: ここでは、クラス
MiscMath
のフィールドdivisor
が型int
であると宣言されていますメソッド・パラメータ(8.4.1): ここでは、メソッド
ratio
のパラメータl
が型long
であると宣言されていますメソッド結果(8.4): ここでは、メソッド
ratio
の結果が型float
であると宣言され、メソッドgausser
の結果が型double
であると宣言されていますコンストラクタ・パラメータ(8.8.1): ここでは、
MiscMath
のコンストラクタのパラメータが型int
であると宣言されていますローカル変数(14.4、14.14): メソッド
gausser
のローカル変数r
およびval
が型Random
およびdouble[]
(double
の配列)であると宣言されています例外パラメータ(14.20): ここでは、
catch
句の例外パラメータe
が型Exception
であると宣言されています型パラメータ(4.4): ここでは、
MiscMath
の型パラメータが、宣言された境界として型Number
を持つ型変数T
であると宣言されていますパラメータ化された型を使用する宣言内: ここでは、パラメータ化された型
Collection<Number>
内の型引数(4.5.1)として型Number
が使用されています。
および次の種類の式内:
クラス・インスタンス作成(15.9): ここでは、型
Random
を使用するクラス・インスタンス作成式により、メソッドgausser
のローカル変数r
が初期化されています汎用クラス(8.1.2)インスタンス作成(15.9): ここでは、式
new ArrayList<Number>()
内の型引数としてNumber
が使用されています配列作成(15.10.1): ここでは、
double
の配列をサイズ2で作成する配列作成式により、メソッドgausser
のローカル変数val
が初期化されています汎用メソッド(8.4.4)またはコンストラクタ(8.8.4)の呼出し(15.12): ここでは、メソッド
loop
が明示的な型引数S
を使用してそれ自体を呼び出していますキャスト(15.16): ここでは、メソッド
ratio
のreturn
文がキャスト内でfloat
型を使用していますinstanceof
演算子(15.20.2): ここでは、instanceof
演算子により、e
が型ArithmeticException
と割当て互換しているかどうかがテストされています
第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)にも関連します。アクセス修飾子がない場合、ほとんどの宣言はパッケージ・アクセス権を持つため、その宣言が含まれていれば、パッケージ内のどの場所でもアクセスできます。その他の選択肢は、public
、protected
およびprivate
です。
この章では、完全修飾名および正規名(6.7)についても説明します。
6.1 宣言
宣言により、エンティティがプログラムに導入され、このエンティティを参照するために名前内で使用できる識別子(3.8)が組み込まれます。この識別子には、導入されるエンティティがクラス、インタフェースまたは型パラメータである場合は型識別子であるという制約があります。
宣言されたエンティティは、次のいずれかです。
module
宣言で宣言されたモジュール(7.7)package
宣言で宣言されたパッケージ(7.4)単一型インポート宣言またはオンデマンド型インポート宣言で宣言される、インポートされる
型クラスまたはインタフェース(7.5.1、7.5.2)単一静的インポート宣言またはオンデマンド静的インポート宣言で宣言された、インポートされた
static
メンバー(7.5.3、7.5.4)汎用クラス、インタフェース、メソッドまたはコンストラクタの宣言の一部として宣言された型パラメータ(8.1.2、9.1.2、8.4.4、8.8.4)
参照型のメンバー(8.2、9.2、8.9.3、9.6、10.7)で、次のいずれか:
enum定数(8.9)enum定数はメンバーではありません。これらは、メンバーである暗黙的なフィールドを導入します。
フィールドで、次のいずれか:
メソッドで、次のいずれか:
enum定数(8.9.1)
パラメータで、次のいずれか:クラスまたはインタフェースのメソッド(8.4.1)、クラスのコンストラクタ(8.8.1)またはラムダ式(15.27.1)の仮パラメータ
try
文のcatch
句で宣言された例外ハンドラの例外パラメータ(14.20)
仮パラメータと例外パラメータは通常、2つの別個のエンティティとして扱います。一方、クラス・メソッドとインタフェース・メソッドの仮パラメータは1箇所のみで指定し、別個のものとして扱う必要はありません。
コンストラクタ(8.8)も宣言によって導入されますが、新しい名前が導入されるのではなく、そのコンストラクタが宣言されたクラスの名前が使用されます。
汎用ではない型の宣言(class C ...
)では、1つのエンティティ(非汎用型(C
))が宣言されます。非汎用型は、構文上の類似点を除いてRAW型ではありません。対照的に、汎用型(class C<T> ...
またはinterface C<T> ...
)の宣言では、汎用型(C<T>
)および対応する非汎用型(C
)の2つのエンティティが宣言されます。この場合、用語C
の意味は、この用語が出現するコンテキストによって異なります。
汎用性が重要ではない場合、次の非汎用コンテキストに示すように、識別子
C
は非汎用型C
を示します。汎用性が重要である場合、非汎用コンテキストを除く6.5のすべてのコンテキストの場合のように、識別子
C
は次のどちらかを示します。
14種類の非汎用コンテキストは、次のとおりです。
モジュール宣言内の
uses
またはprovides
ディレクティブ内(7.7.1)単一型インポート宣言内(7.5.1)
単一静的インポート宣言内の
.
の左側(7.5.3)オンデマンド静的インポート宣言内の
.
の左側(7.5.4)コンストラクタ宣言内の
(
の左側(8.8)注釈内の
@
記号の後(9.7)クラス・リテラル内の
.class
の左側(15.8.2)修飾された
this
式の.this
の左側(15.8.4)修飾されたスーパークラス・フィールド・アクセス式の
.super
の左側(15.11.2)修飾されたメソッド呼出し式内の
.
Identifierまたは.super.
Identifierの左側(15.12)メソッド参照式内の
.super::
の左側(15.13)例外パラメータ宣言内(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.2、9.1.2)の宣言は、C
という名前のクラスと、RAWのC
、C<Foo>
、C<Bar>
などの型のファミリの両方を導入します。非汎用コンテキストの1つとして次に示されている汎用性が重要ではない場所で
C
への参照が発生した場合、C
への参照は、クラスまたはインタフェースC
を示します。その他のコンテキストでは、C
への参照は、C
によって導入された型または型の一部を示します。14種類の非汎用コンテキストは、次のとおりです。
モジュール宣言内の
uses
またはprovides
ディレクティブ内(7.7.1)単一型インポート宣言内(7.5.1)
単一静的インポート宣言内の
.
の左側(7.5.3)オンデマンド静的インポート宣言内の
.
の左側(7.5.4)コンストラクタ宣言内の
(
の左側(8.8)注釈内の
@
記号の後(9.7)クラス・リテラル内の
.class
の左側(15.8.2)修飾された
this
式の.this
の左側(15.8.4)修飾されたスーパークラス・フィールド・アクセス式の
.super
の左側(15.11.2)修飾されたメソッド呼出し式内の
.
Identifierまたは.super.
Identifierの左側(15.12)メソッド参照式内の
.super::
の左側(15.13)例外パラメータ宣言内(14.20)
最初の11の非汎用コンテキストは、6.5.1内のTypeNameの最初の11の構文的コンテキストに対応しています。12番目の非汎用コンテキストは、修飾されたExpressionName (
C.x
など)に、静的メンバー・アクセスを示すためのTypeNameC
が含まれる可能性がある場所です。これらの12のコンテキスト内でTypeNameが共通して使用されていることが重要です。これは、これらのコンテキストには、型のファーストクラス未満の使用方法が含まれることを示します。対照的に、13番目と14番目の非汎用コンテキストではClassTypeが採用されています。これは、throws
およびcatch
句には、型がファーストクラスの方法でフィールド宣言などにあわせて使用されていることを示します。これら2つのコンテキストに非汎用としての特性が与えられるのは、例外型をパラメータ化できないという事実のためです。ClassTypeプロダクションでは注釈が許可されるため、
throws
またはcatch
句内で型の使用に注釈を付けることができます。一方、TypeNameプロダクションでは注釈が許可されないため、単一型インポート宣言などの型の名前に注釈を付けることはできません。
過去において、このドキュメントでの説明を、クラスと(場合によってはパラメータ化された)クラス型およびインタフェースと(場合によってはパラメータ化された)インタフェース型を区別して、より包括的に行うことを試みました。それがこちらの説明になります。現在では、前述の(対応するセクションの)コンテキストの前後にある用語によって、多くの部分で、型が関連しないことが明確になっています。これらは、その型ではなくクラスおよびインタフェースについて記述するコンストラクトです。
この説明は、クラスへの参照と型への参照の区別が明確になるように、導入の段落にいくつか変更を加え、注記としてここに保持されています。
命名規則
Java SEプラットフォームのクラス・ライブラリは、可能なかぎり、次の命名規則に従って選択された名前を使用しようとします。これらの命名規則は、コードをより読みやすくし、ある種の命名規則の競合を回避する上で役に立ちます。
Javaプログラミング言語で書かれたすべてのプログラムで、これらの命名規則を使用することをお薦めします。ただし、以前から使用されてきた慣例的な使用方法とは異なる場合、これらの命名規則に無条件に従う必要はありません。このため、たとえば、クラス
java.lang.Math
のsin
およびcos
メソッドには、ここで提案されている命名規則をこれらのメソッド名が無視する形になるとしても、これらは短く、動詞ではないため、数学的に慣例的な名前が使用されます。
...
クラスおよびインタフェース
型の名前
クラス型クラスの名前は、説明的な名詞または名詞句にし、過度に長くせず、大文字と小文字を混在させ、各単語の先頭を大文字にしてください。
例6.1-3.説明的なクラス名
`ClassLoader`
SecurityManager
`Thread`
Dictionary
BufferedInputStream
同様に、
インタフェース型インタフェースの名前は、短く説明的にし、過度に長くせず、大文字と小文字を混在させ、各単語の先頭を大文字にしてください。この名前は、説明的な名詞または名詞句である場合があります。これは、インタフェースが抽象的なスーパークラスであるかのように使用される場合(java.io.DataInput
やjava.io.DataOutput
など)、またはインタフェースが動作を示す形容詞である場合(インタフェースRunnable
やCloneable
など)である場合に適しています。
型変数名
型変数名は、簡潔(可能であれば1文字)でありながら示唆に富む名前にする必要があります。また、小文字は含めないでください。これにより、型パラメータを一般的なクラスやインタフェースと区別しやすくなります。
コンテナ
型・クラスおよびインタフェースには、要素型として名前E
を使用する必要があります。マップには、キーの型としてK
、および値の型としてV
を使用する必要があります。任意の例外型には、名前X
を使用する必要があります。Oracleでは、型を区別するためにより具体的な名前がない場合は、型にT
を使用しています。(これはたいてい、汎用メソッドの場合です。)
任意の型を示す型パラメータが複数存在する場合、アルファベットで
T
の隣にある文字(S
など)を使用する必要があります。また、異なる型変数を区別するために下付き数字(T1
、T2
など)を使用することも許容されます。このような場合、同じ接頭辞を持つすべての変数に添字を付ける必要があります。
汎用クラス内に汎用メソッドが出現する場合、混乱を避けるために、メソッドおよびクラスの型パラメータに同じ名前を使用しないようにすることをお薦めします。同じことが、ネストした汎用クラスにも当てはまります。
例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文字の範囲内で意味のあるものとして選択する必要があります。前述の名前(
E
、K
、V
、X
、T
)は、指定したカテゴリに該当しない型パラメータには使用しないでください。
...
定数名
インタフェース型インタフェース内の定数の名前は、1つ以上の単語、頭字語または短縮形のシーケンスにし、すべて大文字にし、コンポーネントをアンダースコア「_
」文字で区切ってください。また、クラス型クラスの変数final
の場合も、慣習に従って同様にできます。定数名は説明的なものにし、不必要に省略しないでください。これらは慣習的に、妥当な話し言葉の一部である場合があります。
定数名の例として、クラス
Character
のMIN_VALUE
、MAX_VALUE
、MIN_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.4、4.5.2、4.8、4.9、7.1、8.2、9.2および10.7のメンバーシップの説明を参照してください。
プログラムのすべての識別子が名前の一部となるわけではありません。識別子は、次の状況でも使用されます。
宣言(6.1)。この場合、宣言されるエンティティを識別する名前を指定するために、識別子が出現する可能性があります。
ラベルが付いた文(14.7)と、
break
文およびcontinue
文(14.15、14.16)のラベルとして。この場合、式のラベルを指します。ラベルが付いた文とその関連する
break
文およびcontinue
文で使用される識別子は、宣言で使用される識別子とは、完全に別個のものになります。フィールド・アクセス式(15.11)。この場合、識別子は「
.
」トークンの後に出現し、「.
」トークンの前の式によって示されるオブジェクト、あるいは「.
」トークンの前のsuper
またはTypeName.super
によって示されるオブジェクトのメンバーを示します。一部のメソッド呼出し式(15.12)。この場合、識別子は「
.
」トークンの後で「(
」トークンの前に出現し、「.
」トークンの前の式によって示されるオブジェクト、「.
」トークンの前のTypeNameによって示される型、あるいは「.
」トークンの前のsuper
またはTypeName.super
によって示されるオブジェクトに対して呼び出されるメソッドを示します。一部のメソッド参照式(15.13)。この場合、識別子は「
::
」トークンの後に出現し、「::
」トークンの前の式によって示されるオブジェクト、「::
」トークンの前のTypeNameによって示される型、あるいは「::
」トークンの前のsuper
またはTypeName.super
によって示されるオブジェクトのメソッドを示します。修飾されたクラス・インスタンス作成式(15.9)。この場合、識別子は
new
トークンの右側に出現し、new
トークンの前にある式のコンパイル時の型のメンバーである型を示します。注釈の要素と値のペア(9.7.1)。対応する注釈
型インタフェースの要素の型を示します。
...
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.2、9.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
文のForInit部分の右側にある別の宣言子for
文のExpressionおよびForUpdate部分含まれている文
拡張された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として分類されます。
モジュール宣言内の
exports
またはopens
の右側修飾されたPackageName内の「
.
」の左側
次のコンテキストでは、名前が構文的にTypeNameとして分類されます。
最初の11の非汎用コンテキスト(6.1)クラスまたはインタフェースに名前を付けるには:モジュール宣言内の
uses
またはprovides
ディレクティブ内(7.7.1)単一型インポート宣言内(7.5.1)
単一静的インポート宣言内の
.
の左側(7.5.3)オンデマンド静的インポート宣言内の
.
の左側(7.5.4)コンストラクタ宣言内の
(
の左側(8.8)注釈内の
@
記号の後(9.7)クラス・リテラル内の
.class
の左側(15.8.2)修飾された
this
式の.this
の左側(15.8.4)修飾されたスーパークラス・フィールド・アクセス式の
.super
の左側(15.11.2)修飾されたメソッド呼出し式内の
.
Identifierまたは.super.
Identifierの左側(15.12)メソッド参照式内の
.super::
の左側(15.13)
型が使用されている16のコンテキスト内のReferenceType (配列型内のカッコの左側にあるReferenceType、パラメータ化された型内の<の左側、パラメータ化された型の非ワイルドカード型引数内、またはパラメータ化された型のワイルドカード型引数の
extends
またはsuper
句内を含む)を構成する識別子または点線付きの識別子シーケンスとして(4.11):インタフェース宣言の
extends
句内(9.1.3)汎用クラス、インタフェース、メソッドまたはコンストラクタの型パラメータ宣言の
extends
句内(8.1.2、9.1.2、8.4.4、8.8.4)メソッドのレシーバ・パラメータの型(8.4)
例外パラメータ宣言内の型(14.20)
明示的なコンストラクタ呼出し文、クラス・インスタンス作成式、またはメソッド呼出し式に対する明示的な型引数リスト内(8.8.7.1、15.9、15.12)
非修飾クラス・インスタンス作成式内で、インスタンス化するクラス型として(15.9)、またはインスタンス化する無名クラスの直接スーパークラスまたは直接スーパーインタフェースとして(15.9.5)
配列作成式内の要素型(15.10.1)
キャスト式のキャスト演算子内の型(15.16)
instanceof
関係演算子に続く型(15.20.2)メンバー・メソッドを検索するための参照型として、またはコンストラクトに対するクラス型または配列型としてのメソッド参照式内(15.13)。
前述の16のコンテキスト内のReferenceTypeの識別子からのTypeNameの抽出は、要素型や型引数などのReferenceTypeのサブ用語すべてに繰り返し適用することを意図しています。
たとえば、フィールド宣言に型
p.q.Foo[]
が使用されるとします。配列型のカッコは無視され、用語p.q.Foo
が識別子の点線シーケンスとして配列型内のカッコの左側に抽出され、TypeNameとして分類されます。後のステップで、p
、q
およびFoo
のどれが型名またはパッケージ名であるかが確認されます。別の例として、キャスト演算子に型
p.q.Foo<? extends String>
が使用されるとします。用語p.q.Foo
が識別子用語の点線シーケンスとして再度、今回はパラメータ化された型内の<
の左側に抽出され、TypeNameとして分類されます。用語String
が、パラメータ化された型のワイルドカード型引数のextends
句内の識別子として抽出され、TypeNameとして分類されます。
次のコンテキストでは名前がExpressionNameとして構文的に分類されています。
修飾されたスーパークラス・コンストラクタ呼び出し内で修飾する式として(8.8.7.1)
修飾されたクラス・インスタンス作成式内で修飾する式として(15.9)
配列アクセス式内で配列参照式として(15.10.3)
PostfixExpressionとして(15.14)
割当て演算子の左側のオペランドとして(15.26)
try
-with-resources文内のVariableAccessとして(14.20.3)
次のコンテキストでは、名前が構文的にMethodNameとして分類されます。
- メソッド呼出し式内の「
(
」の前(15.12)
次のコンテキストでは、名前が構文的にPackageOrTypeNameとして分類されます。
修飾されたTypeName内の「
.
」の左側オンデマンド型インポート宣言内(7.5.2)
次のコンテキストでは、名前が構文的にAmbiguousNameとして分類されます。
修飾されたExpressionName内の「
.
」の左側メソッド呼出し式内の「
(
」の前にある右端の.
の左側修飾されたAmbiguousName内の「
.
」の左側注釈
型要素宣言のデフォルト値句内(9.6.2)要素と値のペア内の「
=
」の右側(9.7.1)メソッド参照式内の
::
の左側(15.13)
構文的分類の結果、特定の種類のエンティティを式の特定の部分に制限できるようになります。
フィールド、パラメータまたはローカル変数の名前を式として使用できます(15.14.1)。
メソッドの名前は、式内にメソッド呼出し式の一部としてのみ表示される場合があります(15.12)。
クラスまたはインタフェース
型の名前は、式内に、クラス・リテラルの一部(15.8.2)、修飾されたthis
式(15.8.4)、クラス・インスタンス作成式(15.9)、配列作成式(15.10.1)、キャスト式(15.16)、instanceof
式(15.20.2)、enum定数(8.9)、あるいはフィールドまたはメソッドの修飾名の一部としてのみ出現できます。パッケージの名前は、式内に、クラスまたはインタフェース
型の修飾名の一部としてのみ出現できます。
6.5.4 PackageOrTypeNameの意味
6.5.4.1 単純なPackageOrTypeName
PackageOrTypeName、Qが有効なTypeIdentifierで、かつQという名前の型クラス、インタフェースまたは型パラメータのスコープに出現する場合、PackageOrTypeNameはTypeNameとして再分類されます。
そうでない場合、PackageOrTypeNameはPackageNameとして再分類されます。PackageOrTypeNameの意味は、再分類された名前の意味です。
6.5.4.2 修飾されたPackageOrTypeName
修飾されたPackageOrTypeNameがQ.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.5、9.5)、Q内のIdという名前のメンバー型・クラスまたはインタフェースがアクセス可能でない場合(6.6)、あるいはIdがQ内の複数のメンバー型・クラスまたはインタフェースの名前である場合は、コンパイル時にエラーが発生します。
例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} {
TypeDeclarationTopLevelClassOrInterfaceDeclaration } - ModularCompilationUnit:
- {ImportDeclaration} ModuleDeclaration
通常コンパイル・ユニットは3つの部分で構成され、それぞれがオプションです。
コンパイル・ユニットが属するパッケージの完全修飾名(6.7)を指定する、
package
宣言(7.4)。package
宣言がないコンパイル・ユニットは、名前のないパッケージ(7.4.2)に属します。他のパッケージ内の
型クラスおよびインタフェースや型クラスおよびインタフェースのstatic
メンバーを、それらの単純名を使用して参照できるようにするimport
宣言(7.5)。
モジュラ・コンパイル・ユニットはmodule
宣言(7.7)で構成され、オプションで、その前にimport
宣言が含められます。import
宣言を使用すると、このモジュールおよび他のモジュールのパッケージ内の型クラスおよびインタフェースや型クラスおよびインタフェースのstatic
メンバーを、module
宣言内でそれらの単純名を使用して参照できます。
package
宣言の直後の各コンパイル・ユニットの先頭に宣言import java.lang.*;
が出現する場合と同様に、それぞれのコンパイル・ユニットによって、事前定義済のパッケージjava.lang
で宣言されたそれぞれのpublic
型クラスまたはインタフェース名が暗黙的にインポートされます。その結果、それらの型クラスおよびインタフェースすべての名前をそれぞれのコンパイル・ユニットで単純名として使用できます。
ホスト・システムによって、観察可能なコンパイル・ユニットが特定されます。ただし、事前定義済のパッケージjava
とそのサブパッケージlang
およびio
内のコンパイル・ユニットは除きます(これらはすべて、常に観察可能です)。
それぞれの観察可能なコンパイル・ユニットは、次のようにモジュールに関連付けることができます。
ホスト・システムは、観察可能な通常コンパイル・ユニットが、ホスト・システムによって選択されたモジュールに関連付けられていることを判断できます。ただし、(i)事前定義済のパッケージ
java
とそのサブパッケージlang
およびio
内の通常コンパイル・ユニット(これらはすべて、java.base
モジュールに関連付けられる)、および(ii)名前のないパッケージ内の通常コンパイル・ユニット(これは、7.4.2で指定されたモジュールに関連付けられる)は除きます。ホスト・システムは、観察可能なモジュラ・コンパイル・ユニットがモジュラ・コンパイル・ユニットで宣言されたモジュールに関連付けられていることを判断する必要があります。
コンパイル・ユニットの観察可能性はそのパッケージの観察可能性(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.3、6.5.3、6.5.5)。
前述のルールによって、モジュラ・コンパイル・ユニット内の注釈(特に、モジュール宣言に適用される注釈)で使用される
パッケージ/型の名前が、モジュールに関連付けられた通常コンパイル・ユニットにそれらが出現する場合と同様に解釈されることが確実になります。
異なる通常コンパイル・ユニットで宣言された型クラスおよびインタフェースは、相互を循環的に参照することがあります。Javaコンパイラは、そのようなすべての型クラスおよびインタフェースを同時にコンパイルするように準備する必要があります。
7.5 インポート宣言
インポート宣言を使用すると、名前付き型またはクラス、インタフェースまたはstatic
メンバーを、単一の識別子で構成される単純名(6.2)で参照できます。
適切なインポート宣言を使用しない場合、別のパッケージで宣言された型クラスやインタフェース、または別の型クラスまたはインタフェースのstatic
メンバーを参照するには、完全修飾名(6.7)を使用することが唯一の方法となります一般的に必要になります。
元の記載に関する問題:
メンバー・クラスには、その宣言クラスをインポートすることによって、部分修飾名でアクセスできました。
スーパークラスのメンバーは、インポートする必要なく、継承を介してスコープ内になります。
- ImportDeclaration:
- SingleTypeImportDeclaration
- TypeImportOnDemandDeclaration
- SingleStaticImportDeclaration
- StaticImportOnDemandDeclaration
単一型インポート宣言(7.5.1)では、単一の名前付き
型クラスまたはインタフェースを、その正規名(6.7)を指定することによってインポートします。オンデマンド型インポート宣言(7.5.2)では、
型またはパッケージ、クラスまたはインタフェースの正規名を指定することによって、必要に応じて、名前付き型または名前付きパッケージ、クラスまたはインタフェースのアクセス可能なすべての型クラスおよびインタフェースをインポートします。単一静的インポート宣言(7.5.3)では、特定の名前を持つアクセス可能なすべての
static
メンバーを、型クラスまたはインタフェースの正規名を指定することによってそこからインポートします。オンデマンド静的インポート宣言(7.5.4)では、
型クラスまたはインタフェースの正規名を指定することによって、必要に応じて、名前付き型クラスまたはインタフェースのアクセス可能なすべてのstatic
メンバーをインポートします。
これらの宣言によってインポートされる型クラス、インタフェースまたはメンバーのスコープおよびシャドウ化については、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.5、9.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.5、9.5)が1回のみインポートされた場合と同様の結果になります。
7.6 最上位型クラスおよびインタフェース宣言
最上位型クラスまたはインタフェース宣言では、最上位クラス型(88.1)または最上位インタフェース型(99.1)を宣言します。
TypeDeclaration:TopLevelClassOrInterfaceDeclaration:- ClassDeclaration
- InterfaceDeclaration
;
コンパイル・ユニット内で
型クラスまたはインタフェース宣言のレベルに出現する余分な;
トークンは、コンパイル・ユニットの意味に影響しません。Javaプログラミング言語では、不要なセミコロンは、クラス宣言の後に;
を配置することに慣れているC++プログラマのための便宜的措置としてのみ許容されています。新しいJavaコードでは使用しないでください。
アクセス修飾子がない場合、最上位型クラスまたはインタフェースはパッケージ・アクセス権を持ちます。それが宣言されたパッケージの通常コンパイル・ユニット内でのみアクセス可能です(6.6.1)。型クラスまたはインタフェースをpublic
として宣言すると、同じモジュールの他のパッケージのコードから、また場合によっては他のモジュールのパッケージのコードから型クラスまたはインタフェースへのアクセスを許可できます。
protected
、private
または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
など)で構成される名前のファイルに見つからない場合はコンパイル時にエラーが発生するという制限を強制することを選択できます。
型クラスまたはインタフェースが、その型クラスまたはインタフェースが宣言されているパッケージの他の通常コンパイル・ユニット内のコードによって参照されています。型クラスまたはインタフェースがpublic
として宣言されています(そのため、他のパッケージ内のコードからアクセスできる可能性があります)。
この制限は、1つのコンパイル・ユニットについてそのような
型クラスまたはインタフェースを1つまでにする必要があることを意味します。この制限によって、Javaコンパイラはパッケージ内で名前付きクラスまたはインタフェースを容易に見つけることができます。実際には、多くのプログラマは、各クラスまたはインタフェース型がpublic
であるかどうかや、他のコンパイル・ユニットのコードによって参照されているかどうかに関係なく、クラスまたはインタフェースをその独自のコンパイル・ユニットに配置します。
たとえば、
public
型wet.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
というpublic
なstatic
メソッドを明示的に宣言する場合、そのメソッドはプロバイダ・メソッドと呼ばれます。
サービス・プロバイダにプロバイダ・メソッドがある場合、その戻り型は、(i)現在のモジュール内で宣言されているか、別のモジュール内で宣言されていて、現在のモジュール内のコードからアクセス可能であり、かつ(ii) provides
ディレクティブで指定されたサービスのサブタイプである必要があります。そうでない場合はコンパイル時にエラーが発生します。
provides
ディレクティブによって指定されるサービス・プロバイダは現在のモジュール内で宣言する必要がありますが、そのプロバイダ・メソッドは、別のモジュール内で宣言された戻り型を持つことができます。また、サービス・プロバイダがプロバイダ・メソッドを宣言する場合、サービス・プロバイダ自体がサービスのサブタイプである必要はありません。
サービス・プロバイダにプロバイダ・メソッドがない場合、そのサービス・プロバイダはプロバイダ・コンストラクタを持つ必要があるとともに、provides
ディレクティブで指定されたサービスのサブタイプである必要があります。そうでない場合はコンパイル時にエラーが発生します。
モジュール宣言内の複数のprovides
ディレクティブで同じサービスを指定した場合、コンパイル時にエラーが発生します。
特定のprovides
ディレクティブのwith
句で同じサービス・プロバイダを複数回指定した場合、コンパイル時にエラーが発生します。
第8章: クラス
クラス宣言では、新しい参照型クラスを定義し、これらの実装方法について記述します(8.1)。
最上位クラス(7.6)は、ネストしたクラスではないコンパイル・ユニットの最上位で宣言されるクラスです。
ネストしたクラスは、宣言が別のクラスまたはインタフェースの本体内で行われるクラスです。ネストしたクラスは、メンバー・クラス(8.5、9.5)、ローカル・クラス(14.3)または無名クラス(15.9.5)である場合があります。
内部クラス(8.1.3)は、包含クラス・インスタンス、ローカル変数および型変数を参照できる、ネストしたクラスです。
enumクラス(8.9)は、名前付きクラス・インスタンスの小規模なセットを定義する特別な構文で宣言されるクラスです。
この章では、すべてのクラス、つまり、最上位クラス(7.6)とネストしたクラス(メンバー・クラス(8.5、9.5)、ローカル・クラス(14.3)および無名クラス(15.9.5)を含む)の共通セマンティクスについて説明します。特定の種類のクラスに固有の詳細は、これらのコンストラクトに特化したセクションで説明します。
名前付きクラスはabstract
(8.1.1.1)として宣言でき、これが不完全に実装されている場合は抽象であると宣言する必要があります。このようなクラスはインスタンス化できませんが、サブクラスによって拡張できます。クラスはfinal
(8.1.1.2)として宣言される場合があります。この場合、このクラスはサブクラスを持つことができません。クラスがクラスでは、アクセス制御(6.6)を使用して、他のクラス、インタフェース、パッケージまたはモジュールからのクラスへの参照を防止できます。public
として宣言されている場合、そのモジュールの任意のパッケージ内のコードから、および場合によっては他のモジュール内のコードから参照できます。Object
を除く各クラスは、既存の単一のクラスの拡張(つまり、サブクラス) (8.1.4)であり、インタフェースを実装する場合があります(8.1.5)。クラスは汎用(8.1.2)である場合があります。つまり、クラスは、クラスの様々なインスタンス間でバインディングが異なる可能性のある型変数を宣言できます。
クラス宣言の完全なドメインをより適切に反映するための説明。
クラスは、他の種類の宣言と同じように、注釈(9.7)を使用して修飾できます。
クラスの本体では、メンバー(フィールド、およびメソッド、およびネストしたクラス、およびインタフェース)、インスタンス・イニシャライザ、静的イニシャライザおよびコンストラクタ(8.1.6)を宣言します。メンバー(8.2)のスコープ(6.3)は、メンバーが属するクラスの宣言の本体全体です。フィールド、メソッド、メンバー・クラス、メンバー・インタフェースおよびコンストラクタ宣言には、アクセス修飾子(6.6) public
、protected
または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]
[SuperclassClassExtends] [SuperinterfacesClassImplements] 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.5、9.5)にのみ関連します。ローカル・クラス(14.3)または匿名クラス(15.9.5)には関連しません。
アクセス修飾子protected
、およびprivate
およびstatic
は、直接包含するクラス宣言(8.5)内のメンバー・クラスにのみ関連します。
修飾子static
は、メンバー・クラス(8.5.1)にのみ関連し、最上位クラス、ローカル・クラスまたは匿名クラスには関連しません。
これは包括的なリストまたはルールの場所ではありません。これらの文の目的は、特定の修飾子の使用方法を知る必要があるリーダーに、いくつかの相互参照を提供することです。そこで詳細を確認できます。
クラス宣言について同じキーワードが修飾子として複数回出現する場合、またはアクセス修飾子public
、protected
および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番目の字句的な包含型宣言となるのは、それがCのn-1番目の字句的な包含型宣言を直接包含する型宣言である場合です。
クラスまたはインタフェースOの直接内部クラスCのインスタンスiは、iを直接包含するインスタンスと呼ばれる、Oのインスタンスに関連付けられます。オブジェクトを直接包含するインスタンス(ある場合)は、オブジェクトの作成時に特定されます(15.9.2)。
オブジェクトoは、それ自体のゼロ番目の字句的な包含インスタンスです。
オブジェクトoがインスタンスiのn番目の字句的な包含インスタンスとなるのは、それがiのn-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クラスによってのみ拡張可能)に名前を付けた場合は、コンパイル時にエラーが発生します(8.9)。Enum
の呼出し
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
句が欠落しているクラスの直接スーパークラス型は次のとおりです。
クラス
Object
は直接スーパークラス型を持ちません。標準クラス宣言での
Object
以外のクラスでは、直接スーパークラス型はObject
です。enumクラスEについては、直接スーパークラス型は
Enum<
E>
です。匿名クラスについては、直接スーパークラス型は15.9.5で定義されています。
8で示されているように、このセクションは、すべてのクラスに関するものであるため、すべてのクラスの関係を定義することが重要です。
クラスの直接スーパークラスは、その直接スーパークラス型によって名前が付けられるクラスです。クラスは、直接スーパークラスの直接サブクラスであると言うことができます。直接スーパークラスは、その実装が現在のクラスの実装の導出元であるクラスです。
サブクラススーパークラス関係は、直接サブクラススーパークラス関係の推移閉包です。クラスAがクラスCのサブクラススーパークラスであるのは、次のいずれかの条件が満たされる場合です。
AがCの直接
サブクラススーパークラスであるクラスBのサブクラスがA、かつBがCのサブクラスであるようなBが存在し、クラスBがCの直接スーパークラス、かつAがBのスーパークラスであり、この定義が再帰的に適用される。
クラス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
この場合、これらの関係は次のとおりです。
クラス
Point
は、Object
の直接サブクラスである。クラス
Object
は、クラスPoint
の直接スーパークラスである。クラス
ColoredPoint
は、クラスPoint
の直接サブクラスである。クラス
Point
は、クラスColoredPoint
の直接スーパークラスである。
クラス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; }
この場合、これらの関係は次のとおりです。
クラス
Point
は、クラスColoredPoint
のスーパークラスである。クラス
Point
は、クラスColored3dPoint
のスーパークラスである。クラス
ColoredPoint
は、クラスPoint
のサブクラスである。クラス
ColoredPoint
は、クラスColored3dPoint
のスーパークラスである。クラス
Colored3dPoint
は、クラスColoredPoint
のサブクラスである。クラス
Colored3dPoint
は、クラスPoint
のサブクラスである。
クラスCが型TクラスまたはインタフェースAに直接依存するのは、TAがCのextends
またはimplements
句内で、スーパークラスまたはスーパーインタフェースとして、またはスーパークラスまたはスーパーインタフェース名の完全修飾形式の修飾子として指定されている場合です。
クラスCが参照型TクラスまたはインタフェースAに依存するのは、次のいずれかの条件が満たされる場合です。
Cが
TAに直接依存する。Cが
TAに依存(9.1.3)するインタフェースIに直接依存する。Cが、
TAに依存するクラスDに直接依存する(この定義を再帰的に使用します)。
クラスがそれ自体に依存する場合は、コンパイル時にエラーが発生します。
クラスがロードされるときに、循環的に宣言されたクラスが実行時に検出された場合、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のスーパーインタフェースです。
IがCの直接スーパーインタフェースである。
Cが、9.1.3に示されている「インタフェースのスーパーインタフェース」の定義を使用して、Iがスーパーインタフェースである直接スーパーインタフェースJを持つ。
IがCの直接スーパークラスのスーパーインタフェースである。
1つのクラスが複数の方法でスーパーインタフェースを持つことができます。
クラスは、その直接スーパーインタフェースを直接実装し、そのスーパーインタフェースのすべてを実装すると言い表されます。また、クラスはその直接スーパーインタフェースの直接サブクラスであり、そのすべてのスーパーインタフェースのサブクラスである、と言い表すこともできます。
従来より、サブクラスはクラスとクラスの関係ですが、多くの場合、クラスとインタフェースの関係を説明する名詞を使用すると便利です。「実装クラス」は不自然である場合があります(たとえば、Aがクラスまたはインタフェースであるとして、「CがAのサブクラスである」と「CがAのサブクラスまたは実装クラスである」を比較した場合)。
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; }
}
この場合、これらの関係は次のとおりです。
インタフェース
Paintable
は、クラスPaintedPoint
のスーパーインタフェースです。インタフェース
Colorable
は、クラスColoredPoint
およびクラスPaintedPoint
のスーパーインタフェースです。9.1.3に定義されているとおり、インタフェース
Paintable
はインタフェースColorable
のサブインタフェースであり、Colorable
はPaintable
のスーパーインタフェースです。
クラス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 クラス・メンバー
クラス型のメンバーには、次がすべて含まれます。
直接スーパークラス型(8.1.4)から継承されたメンバー(直接スーパークラス型を持たないクラス
Object
内は除く)直接
スーパーインタフェーススーパーインタフェース型(8.1.5)から継承されるメンバークラスの本体(8.1.6)で宣言されたメンバー
private
として宣言されるクラスのメンバーは、そのクラスのサブクラスによって継承されません。
protected
またはpublic
として宣言されるクラスのメンバーのみが、クラスが宣言されるもの以外のパッケージで宣言されるサブクラスによって継承されます。
コンストラクタ、静的イニシャライザおよびインスタンス・イニシャライザはメンバーではないため、継承されません。
メンバーの型というフレーズを使用して、次のことを示します。
フィールドの場合は、その型です。
メソッドの場合は、次で構成される順序が付けられた4つ組です。
型パラメータ: メソッド・メンバーの型パラメータの宣言。
引数型: メソッド・メンバーに対する引数の型のリスト。
戻り型: メソッド・メンバーの戻り型。
throws
句: メソッド・メンバーのthrows
句で宣言される例外型。
クラス型のフィールド、メソッドおよびメンバー型・クラスおよびインタフェースは、異なるコンテキストで使用され、異なる参照手順(6.5)によって区別されるため、同じ名前を使用できます。ただし、スタイルの面からお薦めできません。
...
8.3 フィールド宣言
クラス型の変数は、フィールド宣言によって導入されます。
...
8.4 メソッド宣言
8.4.8 継承、オーバーライドおよび非表示
クラスCは、その直接スーパークラス型から、スーパークラス型のすべての具象メソッドm (static
とインスタンスの両方) (次がすべて当てはまるもの)を継承します。
mは、Cの直接スーパークラス型Dのメンバーである。
mは、
public
またはprotected
であるか、Cと同じパッケージのパッケージ・アクセス権で宣言されている。Cで宣言されたメソッドは、Dのメンバーとしてのmのシグネチャのサブシグネチャ(8.4.2)であるシグネチャを持たない。
特定の型のメンバーとしてのメソッドのシグネチャに関して、サブシグネチャ・テストを実行することが重要です。メソッドのシグネチャは、型パラメータのインスタンス化に応じて異なります。
クラスCは、その直接スーパークラス型および直接スーパーインタフェーススーパーインタフェース型のすべてのabstract
と、デフォルト(9.4)のメソッドm (次がすべて当てはまるもの)を継承します。
mは、Cの直接スーパークラス型または直接スーパーインタフェース型Dのメンバーである。
mは、
public
またはprotected
であるか、Cと同じパッケージのパッケージ・アクセス権で宣言されている。Cで宣言されたメソッドは、Dのメンバーとしてのmのシグネチャのサブシグネチャ(8.4.2)であるシグネチャを持たない。
Cによってその直接スーパークラス型から継承された具象メソッドは、Dのメンバーとしてのmのシグネチャのサブシグネチャであるシグネチャを持たない。
メソッドm'がメソッドmの宣言をD'のクラスまたはインタフェース(8.4.8.1、9.4.1.1)からオーバーライドするという条件で、Cの直接スーパークラス型または直接スーパーインタフェース型D'のメンバーであるm'は存在しない(mはm'とは異なり、DはD'とは異なる)。
クラスは、そのスーパーインタフェーススーパーインタフェース型から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
のメソッドfoo
がI1
のメソッドfoo
を「I2
から」オーバーライドする(9.4.1.1)かという点です。I1
とI2
はお互いのサブインタフェースではないため、そうはなりません。したがって、クラスTest
の視点からは、I1
からのfoo
の継承は制約を受けません。I2
からのfoo
の継承も同様です。8.4.8.4に示されているとおり、クラスTest
は両方のfoo
メソッドを継承できます。明らかに、これはabstract
として宣言する必要がありますが、そのようにしない場合は、具象メソッドで両方のabstract
foo
メソッドをオーバーライドします。
継承される具象メソッドにより、
abstract
またはデフォルトのメソッドの継承を阻止できます。(8.4.8.1と9.4.1.1に示されているとおり、具象メソッドは、abstract
またはデフォルトのメソッドを「Cから」オーバーライドします。)また、あるスーパータイプ・メソッドが別のスーパータイプ・メソッドを「すでに」オーバーライドしている場合、前者によって後者の継承を阻止できます。これはインタフェースのルール(9.4.1)と同じであり、複数のデフォルトのメソッドが継承されて、ある実装が明確に他の実装に取って代わるようになる競合が回避されます。
8.4.8.1 オーバーライド(インスタンス・メソッドを使用)
次のすべてのことが当てはまる場合に限り、クラスCで宣言または継承されたインスタンス・メソッドmCは、クラスAで宣言された別のメソッドmAをCからオーバーライドします。
Cは、Aのサブクラスである。
Cは、mAを継承しない。
mCのシグネチャは、名前がAのCのスーパータイプのメンバーとしてのmAのシグネチャのサブシグネチャ(8.4.2)である。
汎用クラスAの場合、シグネチャを比較する前にAに適用する型パラメータを把握する必要があります。
次のいずれかがtrue:
mAは
public
である。mAは
protected
である。mAはCと同じパッケージのパッケージ・アクセス権で宣言され、CがmCを宣言するか、mAがCの直接スーパークラス型のメンバーである。
mAはパッケージ・アクセス権で宣言され、mCはmAをCのなんらかのスーパークラスからオーバーライドする。
mAはパッケージ・アクセス権で宣言され、mCはメソッドm'をCからオーバーライドし(m'はmCおよびmAとは異なる)、m'はmAをCのなんらかのスーパークラスからオーバーライドする。
mCが非abstract
で、abstract
メソッドmAをCからオーバーライドする場合、mCは、mAをCから実装すると言い表されます。
オーバーライドされるメソッドmAがstatic
メソッドの場合、コンパイル時にエラーが発生します。
この点に関しては、メソッドのオーバーライドはフィールド(8.3)の非表示とは異なります。インスタンス変数は
static
変数を隠すことができるためです。
次のすべてのことが当てはまる場合に限り、クラスCで宣言または継承されたインスタンス・メソッドmCは、インタフェースIで宣言された別のメソッドmIをCからオーバーライドします。
Iは、Cのスーパーインタフェースである。
mIは
static
ではない。Cは、mIを継承しない。
mCのシグネチャは、名前がIのCのスーパータイプのメンバーとしてのmIのシグネチャのサブシグネチャ(8.4.2)である。
mIは
public
である。
いずれかのメソッドの仮パラメータがRAW型を持ち、他のメソッドの対応するパラメータがパラメータ化された型を持つ場合、オーバーライドするメソッドのシグネチャは、オーバーライドされるメソッドのシグネチャとは異なる場合があります。これにより、既存のコードの移行に対応し、汎用を活用できます。
オーバーライドの概念には、その宣言クラスのなんらかのサブクラスから別のメソッドをオーバーライドするメソッドが含まれます。これは、2つの方法で考えられます。
特定のパラメータ化において、汎用スーパークラスの具象メソッドは、そのクラスの抽象メソッドと同じシグネチャを持つことができます。この場合、具象メソッドは継承されますが
abstract
メソッドは継承されません(前述を参照)。この場合、継承されるメソッドがその抽象ピアをCからオーバーライドするとみなされます。(このシナリオは、パッケージ・アクセス権によって複雑になります。Cが異なるパッケージに含まれる場合、mAが継承されることはなく、オーバーライドされるとはみなされません。)クラスから継承されるメソッドにより、スーパーインタフェース・メソッドをオーバーライドできます。(幸い、パッケージ・アクセス権はここでは懸念事項ではありません。)
オーバーライドされるメソッドには、キーワードsuper
を含むメソッド呼出し式(15.12)を使用してアクセスできます。修飾名またはスーパークラス型へのキャストは、オーバーライドされるメソッドへのアクセスの試行では、効果的ではありません。
この点に関しては、メソッドのオーバーライドはフィールドの非表示とは異なります。
strictfp
修飾子があるかどうかは、オーバーライドするメソッドと実装する抽象メソッドのルールにはまったく影響しません。たとえば、FP-strictではないメソッドはFP-strictメソッドをオーバーライドでき、FP-strictメソッドはFP-strictではないメソッドをオーバーライドできます。
...
8.4.8.2 非表示(クラス・メソッドを使用)
クラスCがstatic
メソッドmを宣言または継承する場合、mは、クラスまたはインタフェースAで宣言されたメソッドm' (次がすべて当てはまるもの)を隠すと言い表されます。
Aは、Cのスーパークラスまたはスーパーインタフェースである。
m'は、Cのスーパークラスで宣言されたAがインタフェースの場合に、m'がインスタンス・メソッドである。static
またはインスタンス・メソッドであるか、Cのスーパーインタフェースで宣言されたインスタンス・メソッドである。m'はCにアクセス可能である(6.6)。
mのシグネチャは、名前がAのCのスーパータイプのメンバーとしてのm'のシグネチャ
(8.4.2)のサブシグネチャ(8.4.2)である。
シグネチャを特定する際に使用する型についての説明は、型パラメータを使用できない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 オーバーライドおよび非表示の要件
戻り型R1のd1メソッド宣言が、戻り型R2の別のメソッドd2の宣言をオーバーライドするか隠す場合、d1はd2に対して戻り型置換可能(8.4.5)である必要があります。そうでない場合、コンパイル時にエラーが発生します。
このルールにより、オーバーライド時にメソッドの戻り型を改良する共変の戻り型が可能になります。
R1がR2のサブタイプではない場合、@SuppressWarnings
(9.6.4.5)によって非表示にされていないかぎり、コンパイル時に未チェック警告が発生します。
別のメソッドをオーバーライドまたは非表示にするメソッド(インタフェースで定義されたabstract
メソッドを実装するメソッドを含む)は、チェックされた例外をオーバーライド済または非表示のメソッドよりも多くスローするように宣言されない可能性があります。
この点に関しては、メソッドのオーバーライドはフィールド(8.3)の非表示とは異なります。フィールドは他の型のフィールドを隠すことができるためです。
より正確に言うために、Bはクラスまたはインタフェース、AはBのスーパークラスまたはスーパーインタフェースで、Bのメソッド宣言m2はAのメソッド宣言m1をオーバーライドするか隠すとします。次に、
m2が、チェックされる例外型を指定する
throws
句を持つ場合、m1にはthrows
句が必要です。そうでない場合、コンパイル時にエラーが発生します。m2の
throws
句にリストされたすべてのチェックされる例外型については、同じ例外クラスまたはそのスーパータイプのいずれかが、m1のthrows
句のイレイジャ(4.6)に出現する必要があります。そうでない場合、コンパイル時にエラーが発生します。m1の消去されていない
throws
句にm2のthrows
句の各例外型のスーパータイプ(必要な場合はm1の型パラメータに適合します)が含まれている場合、@SuppressWarnings
(9.6.4.5)によって非表示にされていないかぎり、コンパイル時に未チェック警告が発生します。
型宣言TクラスまたはインタフェースCがメンバー・メソッドm1を持ち、TCで宣言されたメソッドm2またはTCのスーパータイプスーパークラスまたはスーパーインタフェースAが存在し、次のことがすべて当てはまる場合は、コンパイル時にエラーが発生します。
m1とm2が同じ名前を持つ。
m2に
TCからアクセス可能である(6.6)。m1のシグネチャは、名前がAのCのスーパータイプのメンバーとしてのm2のシグネチャのサブシグネチャ(8.4.2)ではない。
m1の宣言されたシグネチャまたは一部のメソッドm1のオーバーライド(直接または間接的)は、m2の宣言されたシグネチャまたは一部のメソッドm2のオーバーライド(直接または間接的)と同じイレイジャを持つ。
イレイジャを介して汎用が実装されるため、これらの制限が必要となります。前述のルールは、同じクラスで宣言された同じ名前のメソッドは異なるイレイジャを持つ必要があることを示しています。また、
型宣言クラスまたはインタフェースは、同じ汎用インタフェースの2つの別個の呼出しパラメータ化を実装したり拡張することができないことも示しています。
オーバーライドするメソッドまたは隠すメソッドのアクセス修飾子は、次のように、少なくともオーバーライドされるメソッドまたは隠されるメソッドと同程度のアクセスを提供する必要があります。
オーバーライドされるメソッドまたは隠されるメソッドが
public
の場合、オーバーライドするメソッドまたは隠すメソッドはpublic
である必要があります。そうでない場合、コンパイル時にエラーが発生します。オーバーライドされるメソッドまたは隠されるメソッドが
protected
の場合、オーバーライドするメソッドまたは隠すメソッドはprotected
またはpublic
である必要があります。そうでない場合、コンパイル時にエラーが発生します。オーバーライドされるメソッドまたは隠されるメソッドにパッケージ・アクセス権がある場合、オーバーライドするメソッドまたは隠すメソッドは
private
ではない必要があります。そうでない場合、コンパイル時にエラーが発生します。
これらの用語の技術的な意味においては、
private
メソッドは、オーバーライドすることも、隠すこともできません。このことは、サブクラスはそのいずれかのスーパークラスのprivate
メソッドと同じシグネチャを持つメソッドを宣言でき、そのようなメソッドの戻り型またはthrows
句は、スーパークラスのprivate
メソッドの対応する戻り型または句との関係を持つ必要はないことを意味します。
...
8.5 メンバー型・クラスおよびインタフェース宣言
メンバー・クラスは、宣言が別のクラスまたはインタフェース宣言の本体(8.1.6、9.1.4)に直接包含されているクラスです。メンバー・クラスはenumクラス(8.9)である場合があります。
メンバー・インタフェースは、宣言が別のクラスまたはインタフェース宣言の本体(8.1.6、9.1.4)に直接包含されているインタフェースです。メンバー・インタフェースは注釈インタフェース(9.6)である場合があります。
クラス内のメンバー型・クラスまたはインタフェース宣言のアクセス可能性は、そのアクセス修飾子によって指定され、アクセス修飾子がない場合は6.6によって指定されます。
クラス内のメンバー型宣言について同じキーワードが修飾子として複数回出現する場合、またはアクセス修飾子public
、protected
およびprivate
(6.6)のうちの複数がメンバー型宣言に含まれる場合、コンパイル時にエラーが発生します。
メンバー型・クラスまたはインタフェースのスコープおよびシャドウ化については、6.3および6.4に規定されています。
特定の名前を持つメンバー型・クラスまたはインタフェースをクラスで宣言した場合、その型クラスまたはインタフェースの宣言は、クラスのスーパークラスおよびスーパーインタフェース内の同じ名前を持つメンバー型・クラスおよびインタフェースのアクセス可能なすべての宣言を隠すとされています。
この点に関しては、メンバー
型・クラスおよびインタフェースを隠すことは、フィールド(8.3)を隠すことと同様です。
クラスは、その直接スーパークラスおよび直接スーパーインタフェースから、スーパークラスおよびスーパーインタフェースの非private
メンバー型・クラスおよびインタフェースのうち、クラス内のコードからアクセス可能で、かつクラス内の宣言によって隠されていないものをすべて継承します。
クラスは、そのスーパークラスとスーパーインタフェースから、またはそのスーパーインタフェースのみから、同じ名前を持つ複数のメンバー型・クラスまたはインタフェースを継承することがあります。そのような状況自体によってコンパイル時にエラーが発生することはありません。ただし、クラスの本体内でそのようなメンバー型・クラスまたはインタフェースをその単純名で参照しようとする場合、参照があいまいであるため、コンパイル時にエラーが発生します。
複数のパスによって、同じメンバー型・クラスまたはインタフェース宣言が1つのインタフェースから継承されることがあります。そのような状況では、そのメンバー型・クラスまたはインタフェースは1回のみ継承されるとみなされ、あいまいになることなく、その単純名で参照できます。
8.5.1 staticメンバー型・クラスおよびインタフェース宣言
static
キーワードは、非内部クラスまたはインタフェースTの本体内のメンバー型・クラスCの宣言を変更する場合があります。この結果、Cが内部クラスではないことを宣言します。Tのstatic
メソッドの本体内に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])
;
- TypeArguments:
<
TypeArgumentList>
- ArgumentList:
- Expression {
,
Expression}
明示的なコンストラクタ呼出し文は、次の2種類に分類されます。
代替コンストラクタ呼出しは、キーワード
this
で始まります(明示的な型引数が先頭に付くこともあります)。これらは、同じクラスの代替コンストラクタを呼び出す場合に使用します。スーパークラス・コンストラクタ呼出しは、キーワード
super
で始まるか(明示的な型引数が先頭に付くこともあります)、Primary式またはExpressionNameで始まります。これらは、直接スーパークラスのコンストラクタを呼び出す場合に使用します。これらは、さらに次のように分類されます。修飾されていないスーパークラス・コンストラクタ呼出しは、キーワード
super
で始まります(明示的な型引数が先頭に付くこともあります)。修飾されたスーパークラス・コンストラクタ呼出しは、Primary式またはExpressionNameで始まります。これらを使用すると、直接スーパークラスに関して、新しく作成されるオブジェクトを直接包含するインスタンス(8.1.3)をサブクラス・コンストラクタで明示的に指定できます。これは、スーパークラスが内部クラスである場合に必要になることがあります。
コンストラクタ本体内の明示的なコンストラクタ呼出し文では、このクラスやスーパークラスで宣言されたインスタンス変数、インスタンス・メソッドまたは内部クラスを参照したり、式でthis
またはsuper
を使用することはできません。そのようにすると、コンパイル時にエラーが発生します。
このように現在のインスタンスの使用が禁止されていることが、明示的なコンストラクタ呼出し文が静的コンテキストに出現する(8.1.3)とみなされる理由となっています。
TypeArgumentsがthis
またはsuper
の左側にある場合、いずれかの型引数がワイルドカードであれば(4.5.1)、コンパイル時にエラーが発生します。
Cはインスタンス化されるクラスであり、SはCの直接スーパークラスであるとします。
スーパークラス・コンストラクタ呼出し文が修飾されていない場合、次のようになります。
- Sが内部メンバー・クラスであるが、SがCを包含するクラスのメンバーでない場合は、コンパイル時にエラーが発生します。
スーパークラス・コンストラクタ呼出し文が修飾されている場合、次のようになります。
Sが内部クラスでない場合、またはSの宣言が静的コンテキストに出現する場合は、コンパイル時にエラーが発生します。
また、pは
.super
の直前のPrimary式またはExpressionNameであり、OはSを直接包含するクラスであるとします。pの型がOまたはOのサブクラスでない場合、あるいはpの型にアクセスできない場合は(6.6)、コンパイル時にエラーが発生します。
明示的なコンストラクタ呼出し文がスローできる例外型については、11.2.2に規定されています。
代替コンストラクタ呼出し文の評価では、まず、通常のメソッド呼出しと同様に、コンストラクタの引数が左から右に評価され、続いてコンストラクタが呼び出されます。
スーパークラス・コンストラクタ呼出し文の評価は、次のように進められます。
iは、作成されるインスタンスであるとします。S (ある場合)に関してiを直接包含するインスタンスを特定する必要があります。
Sが内部クラスでない場合、またはSの宣言が静的コンテキストに出現する場合、Sに関してiを直接包含するインスタンスは存在しません。
スーパークラス・コンストラクタ呼出しが修飾されていない場合、Sは必然的にローカル・クラスまたは内部メンバー・クラスです。
Sがローカル・クラスである場合、Oは、Sを直接包含する
型クラスまたはインタフェース宣言であるとします。Sが内部メンバー・クラスである場合、Oは、SがメンバーであるCの最も内側の包含クラスであるとします。
nは、OがCのn番目の字句的な包含
型クラスまたはインタフェース宣言であるような整数であるとします(n ≥ 1)。Sに関してiを直接包含するインスタンスは、
this
のn番目の字句的な包含インスタンスです。継承によってSがCのメンバーである場合もありますが、
this
のゼロ番目の字句的な包含インスタンス(つまり、this
自体)が、Sに関してiを直接包含するインスタンスとして使用されることはありません。スーパークラス・コンストラクタ呼出しが修飾されている場合、
.super
の直前のPrimary式またはExpressionName、pが評価されます。pが
null
と評価されると、NullPointerException
が発生し、スーパークラス・コンストラクタ呼出しは突然完了します。それ以外の場合は、この評価の結果が、Sに関してiを直接包含するインスタンスです。
S (ある場合)に関してiを直接包含するインスタンスが特定されたら、スーパークラス・コンストラクタ呼出し文の評価では、通常のメソッド呼出しと同様に、コンストラクタの引数が左から右に評価され、続いてコンストラクタが呼び出されます。
最後に、スーパークラス・コンストラクタ呼出し文が正常に完了すると、Cのすべてのインスタンス変数イニシャライザとCのすべてのインスタンス・イニシャライザが実行されます。インスタンス・イニシャライザまたはインスタンス変数イニシャライザIのテキストが別のインスタンス・イニシャライザまたはインスタンス変数イニシャライザJの前にある場合、IはJより先に実行されます。
スーパークラス・コンストラクタ呼出しが実際に明示的なコンストラクタ呼出し文として出現するか、暗黙的に提供されるかに関係なく、インスタンス変数イニシャライザおよびインスタンス・イニシャライザの実行は実施されます。(代替コンストラクタ呼出しでは、こうした追加の暗黙的な実行は実施されません。)
例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 [SuperinterfacesClassImplements] EnumBody
enum宣言では、最上位enumクラス(7.6)またはメンバーenumクラス(8.5、9.5)を指定できます。
enum宣言に修飾子abstract
またはfinal
が含まれている場合、コンパイル時にエラーが発生します。
enum宣言は、クラス本体を持つenum定数(8.9.1)を1つ以上含んでいる場合を除き、暗黙的にfinal
です。
ネストしたメンバーenum型クラスは、暗黙的にstatic
です。ネストしたメンバーenum型クラスの宣言ではstatic
修飾子を重複して指定することが許可されています。
これは、内部クラス(8.1.3)は、定数変数を除いて
static
メンバーを持つことができないため、内部クラスの本体でのメンバーとしてenum型クラスを宣言できないことを意味します。
enum宣言について同じキーワードが修飾子として複数回出現する場合、またはアクセス修飾子public
、protected
およびprivate
(6.6)のうちの複数がenum宣言に含まれる場合、コンパイル時にエラーが発生します。
enum宣言はextends
句を持ちません。enum型クラスEの直接スーパークラス型は、Enum<
E>
です(8.1.4)。
enum型クラスには、そのenum定数によって定義されたもの以外のインスタンスはありません。enum型クラスを明示的にインスタンス化しようとすると(15.9.1)、コンパイル時にエラーが発生します。
enum
型クラスのインスタンスが、そのenum定数によって定義されたもの以外に存在することがないようにするために、コンパイル時エラーに加えて、さらに次の3つのメカニズムが用意されています。
Enum
のfinal
clone
メソッドによって、enum定数を一切クローニングできないことが確実化されます。enum
型クラスの反射型のインスタンス化は禁止されています。直列化メカニズムによる特別な処理によって、直列化復元の結果として重複するインスタンスが作成されることがなくなります。
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に規定されています。
EnumConstantのIdentifierは、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宣言Eにabstract
メソッド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のメンバーは、次のすべてです。
Eの宣言の本体で宣言されたメンバー。
Enum<
E>
から継承されたメンバー。Eの宣言の本体で宣言されたそれぞれのenum定数cについて、Eには、cと同じ名前を持つ型Eの暗黙的に宣言された
public
static
final
フィールドがあります。このフィールドは、Eをインスタンス化し、Eについて選択されたコンストラクタにcの引数を渡す変数イニシャライザを持ちます。このフィールドは、cと同じ注釈を持ちます(ある場合)。これらのフィールドは、Eの宣言の本体で
static
フィールドが明示的に宣言される前に、対応するenum定数と同じ順序で暗黙的に宣言されます。enum定数は、対応する暗黙的に宣言されたフィールドが初期化されるときに作成されるとされています。
Eの宣言の本体に出現するのと同じ順序でEのenum定数を含む配列を返す、暗黙的に宣言されたメソッド
public
static
E[]
values()
。指定された名前を持つEのenum定数を返す、暗黙的に宣言されたメソッド
public
static
EvalueOf(String name)
。暗黙的に宣言された次のメソッド。
/** * Returns an array containing the constants of this enum
* type, in the order they're declared. This method may be
* class, in the order they're declared. This method may be
* used to iterate over the constants as follows: * * for(E c : E.values()) * System.out.println(c); * * @return an array containing the constants of this enum
* type, in the order they're declared
* class, in the order they're declared
*/ public static E[] values(); /**
* Returns the enum constant of this type with the specified
* Returns the enum constant of this class with the specified
* name. * The string must match exactly an identifier used to declare
* an enum constant in this type. (Extraneous whitespace
* an enum constant in this class. (Extraneous whitespace
* characters are not permitted.) * * @return the enum constant with the specified name
* @throws IllegalArgumentException if this enum type has no
* @throws IllegalArgumentException if this enum class has no
* constant with the specified name */ public static E valueOf(String name);
つまり、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.2のCoin
型クラスに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.5、9.5)として出現するインタフェースです。
標準インタフェースと注釈型という2種類のインタフェースを区別します。
注釈インタフェース(9.6)は、注釈(9.7)の反射型の表現によって実装することを目的とした、特別な構文で宣言されるインタフェースです。
この章では、すべてのインタフェース、つまり、標準インタフェース(最上位インタフェース(7.6)とネストしたインタフェース(8.5、9.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]
[ExtendsInterfacesInterfaceExtends] InterfaceBody
用語ExtendsInterfacesは、プロダクションがインタフェース型ではなく、インタフェースのリストであると誤って提案しています。用語InterfaceExtendsは、8.1のClassExtendsに合せたものです。
インタフェース宣言の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.5、9.5)にのみ関連します。
修飾子static
は、メンバー・クラス(8.5.1, 9.5)にのみ関連し、最上位インタフェース(7.6)には関連しません。
8.1.1と変更内容を比較してください。
同一のキーワードがあるインタフェース宣言の1つの修飾子として複数回出現する、またはインタフェース宣言にアクセス修飾子public
、protected
、および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のスーパーインタフェースです。
KがIの直接スーパーインタフェースである。
JがIの直接スーパーインタフェースである場合に、KがJのスーパーインタフェースであり、
かつJがIのスーパーインタフェースであるようなインタフェースJが存在し、この定義が再帰的に適用される。
KがIのスーパーインタフェースである場合は常に、インタフェースIはKのサブインタフェースであると言われます。インタフェースはその直接スーパーインタフェースの直接サブインタフェースであり、その各スーパーインタフェースのサブインタフェースであると言い表されます。
すべてのクラスがクラスObject
の拡張ですが、すべてのインタフェースが拡張である単一のインタフェースは存在しません。
インタフェースIが型TクラスまたはインタフェースAに直接依存するのは、TAがIのextends
句内で、スーパーインタフェースとして、またはスーパーインタフェース名の完全修飾形式の修飾子として指定されている場合です。
インタフェースIが参照型TクラスまたはインタフェースAに依存するのは、次のいずれかの条件が満たされる場合です。
Iが
TAに直接依存する。Iが、
TA (8.1.5)に依存するクラスCに直接依存する。Iが、
TAに依存するインタフェースJに直接依存する(この定義を再帰的に使用します)。
インタフェースがそれ自体に依存する場合、コンパイル時にエラーが発生します。
インタフェースがロードされるときに、循環的に宣言されたインタフェースが実行時に検出された場合、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 インタフェース・メンバー
インタフェース型のメンバーは、次のとおりです。
インタフェースの本体(9.1.4)で宣言されたメンバー。
直接
スーパーインタフェーススーパーインタフェース型(9.1.3)から継承されるメンバー。インタフェースが直接
スーパーインタフェーススーパーインタフェース型を持たない場合、そのインタフェースは、Object
(4.3.2)で宣言されたシグネチャs、戻り型rおよびthrows
句tを持つそれぞれのpublic
インスタンス・メソッドmに対応する、シグネチャs、戻り型rおよびthrows
句tを持つpublic
abstract
メンバー・メソッドmを暗黙的に宣言します。ただし、同じシグネチャ、同じ戻り型および互換性のあるthrows
句を持つabstract
メソッドがそのインタフェースによって明示的に宣言される場合は除きます。mが
Object
でfinal
になるように宣言される場合に、インタフェースがそのようなメソッドmを明示的に宣言すると、コンパイル時にエラーが発生します。インタフェースが、
Object
のpublic
メソッドとオーバーライド等価(8.4.2)であるシグネチャを持つメソッドを明示的に宣言するが、その戻り型が異なるか、throws
句の互換性がないか、abstract
ではない場合、コンパイル時にエラーが発生します。
インタフェースは、それによって拡張されるインタフェースから、そのすべてのメンバーを継承します。ただし、(i)それによって隠されるフィールド、クラスおよびインタフェース、(ii)それによってオーバーライドされるabstract
メソッドおよびデフォルトのメソッド(9.4.1)、(iii) private
メソッドおよび(iv) static
メソッドは除きます。
インタフェース型のフィールド、メソッドおよびメンバー型・クラスおよびインタフェースは、異なるコンテキストで使用され、異なる参照手順(6.5)によって区別されるため、同じ名前を使用できます。ただし、スタイルの面からお薦めできません。
9.4 メソッド宣言
9.4.1 継承およびオーバーライド
インタフェースIは、その直接スーパーインタフェーススーパーインタフェース型のすべてのabstract
と、デフォルトのメソッドm (次がすべて当てはまるもの)を継承します。
mは、Iの直接スーパーインタフェース型Jのメンバーである。
Iで宣言されたメソッドは、Jのメンバーとしてのmのシグネチャのサブシグネチャ(8.4.2)であるシグネチャを持たない。
m'がメソッドmの宣言をJ'のインタフェースからオーバーライドするという条件で、Iの直接スーパーインタフェース型J'のメンバーであるメソッドm'は存在しない(mはm'とは異なり、JはJ'とは異なる)。
メソッドは、シグネチャごとにオーバーライドされます。たとえば、インタフェースが同じ名前の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 {}
Right
はTop
からname()
を継承しますが、Bottom
はRight
ではなくLeft
からname()
を継承します。これは、Left
からのname()
がTop
のname()
の宣言をオーバーライドするためです。
インタフェースは、そのスーパーインタフェースからprivate
またはstatic
メソッドを継承しません。
インタフェースIがprivate
または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
メソッドは、スーパーインタフェース型のインスタンス・メソッド(public
かprivate
かは問わない)をオーバーライドできません。このことは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で宣言された別のインスタンス・メソッドmJをIからオーバーライドします。
Iは、Jのサブインタフェースである。
Iは、mJを継承しない。
mIのシグネチャは、名前がJのIのスーパータイプのメンバーとしてのmJのシグネチャのサブシグネチャ(8.4.2)である。
mJは
public
である。
strictfp
修飾子があるかどうかは、オーバーライドするメソッドのルールにはまったく影響しません。たとえば、FP-strictではないメソッドはFP-strictメソッドをオーバーライドでき、FP-strictメソッドはFP-strictではないメソッドをオーバーライドできます。
オーバーライドされるデフォルトのメソッドには、スーパーインタフェース名で修飾されたキーワード
super
を含むメソッド呼出し式(15.12)を使用してアクセスできます。
9.5 メンバー型・クラスおよびインタフェース宣言
インタフェースには、メンバー型・クラスおよびインタフェース宣言(8.5)を含めることができます。
インタフェースの本体内のそれぞれのメンバー型・クラスまたはインタフェース宣言は、暗黙的にpublic
かつstatic
です。これらの修飾子のいずれかまたは両方を重複して指定することが許可されています。
インタフェースのメンバー型・クラスまたはインタフェース宣言に修飾子protected
またはprivate
が含まれている場合、コンパイル時にエラーが発生します。
インタフェースのメンバー型宣言について同じキーワードが修飾子として複数回出現する場合、コンパイル時にエラーが発生します。
特定の名前を持つメンバー型・クラスまたはインタフェースをインタフェースで宣言した場合、その型クラスまたはインタフェースの宣言は、インタフェースのスーパーインタフェース内の同じ名前を持つメンバー型・クラスおよびインタフェースのアクセス可能なすべての宣言を隠すとされています。
インタフェースは、その直接スーパーインタフェースから、スーパーインタフェースの非メンバーprivate
型・クラスおよびインタフェースのうち、インタフェース内のコードからアクセス可能で、かつインタフェース内の宣言によって隠されていないものをすべて継承します。
スーパーインタフェースのメンバー・クラスおよびインタフェースはすべてpublic
であるため、それらのアクセス可能性をここで考慮する必要はありません。
インタフェースは、同じ名前を持つ複数のメンバー型・クラスまたはインタフェースを継承することがあります。そのような状況自体によってコンパイル時にエラーが発生することはありません。ただし、インタフェースの本体内でそのようなメンバー型・クラスまたはインタフェースをその単純名で参照しようとする場合、参照があいまいであるため、コンパイル時にエラーが発生します。
複数のパスによって、同じメンバー型・クラスまたはインタフェース宣言が1つのインタフェースから継承されることがあります。そのような状況では、そのメンバー型・クラスまたはインタフェースは1回のみ継承されるとみなされ、あいまいになることなく、その単純名で参照できます。
9.6 注釈型インタフェース
注釈型宣言では、特別な種類のインタフェース型である、新しい注釈型インタフェースを指定します。注釈型宣言を標準インタフェース宣言と区別するために、キーワードinterface
の前にアットマークアットマーク(@
)が付けられます。
AnnotationTypeDeclaration:AnnotationDeclaration:- {InterfaceModifier}
@
interface
TypeIdentifierAnnotationTypeBodyAnnotationInterfaceBody
アットマークアットマーク(@
)とキーワードinterface
は、別個のトークンです。それらを空白で区切ることもできますが、スタイルとしてはお薦めしません。
注釈型宣言の注釈修飾子に関するルールについては、9.7.4および9.7.5に規定されています。
注釈型宣言のTypeIdentifierでは、注釈型インタフェースの名前を指定します。
注釈型インタフェースの単純名がその包含クラスまたはインタフェースのいずれかと同じ場合、コンパイル時にエラーが発生します。
それぞれの注釈型インタフェースの直接スーパーインタフェース型はjava.lang.annotation.Annotation
です(9.1.3)。
AnnotationTypeDeclarationAnnotationInterfaceDeclaration構文により、注釈型インタフェース宣言を汎用にすることはできず、extends
句も許可されていません。
注釈
型インタフェースではスーパークラス型またはスーパーインタフェース型を明示的に宣言できないため、注釈型インタフェースのサブクラスまたはサブインタフェース自体が注釈型インタフェースになることはありません。同様に、java.lang.annotation.Annotation
自体も注釈型インタフェースにはなりません。
注釈型インタフェースは、様々なメンバーをjava.lang.annotation.Annotation
から継承します。これには、Object
のインスタンス・メソッドに対応する暗黙的に宣言されたメソッドも含まれますが、これらのメソッドでは、注釈型インタフェースの要素(9.6.1)は定義されません。
これらのメソッドでは注釈
型インタフェースの要素が定義されないため、その型の注釈(9.7)でそれらを使用することは不正です。このルールがなければ、要素が注釈で表現できる型であることや、それらのアクセサ・メソッドを使用できることを確認できません。
ここで明示的に変更されていないかぎり、標準インタフェース宣言に適用されるすべてのルールが注釈型宣言にも適用されます。
たとえば、注釈
型インタフェースは、標準クラスおよびインタフェース型クラスおよびインタフェースと同じネームスペースを共有します。また、注釈型宣言は、インタフェース宣言が正当である場合は常に正当であり、インタフェース宣言と同じスコープおよびアクセス可能性を持ちます。
9.6.1 注釈型要素
注釈型宣言の本体にはメソッド宣言を含めることができ、それぞれの宣言で、注釈型インタフェースの要素を定義します。注釈型インタフェースは、それが明示的に宣言するメソッドで定義されたもの以外の要素を持ちません。
AnnotationTypeBody:AnnotationInterfaceBody:{
{AnnotationTypeMemberDeclarationAnnotationMemberDeclaration }}
AnnotationTypeMemberDeclaration:AnnotationMemberDeclaration:AnnotationTypeElementDeclarationAnnotationElementDeclaration- ConstantDeclaration
- ClassDeclaration
- InterfaceDeclaration
;
AnnotationTypeElementDeclaration:AnnotationElementDeclaration:- {
AnnotationTypeElementModifierAnnotationElementModifier } UnannType Identifier(
)
[Dims]
[DefaultValue];
AnnotationTypeElementModifier:AnnotationElementModifier:- (次のうちの1つ)
- Annotation
public
abstract
AnnotationTypeElementDeclarationAnnotationElementDeclarationプロダクションにより、注釈型宣言内のメソッド宣言は、仮パラメータ、型パラメータまたはthrows
区を持つことができません。便宜上、ここでは4.3の次のプロダクションを示します。
- Dims:
- {Annotation}
[
]
{{Annotation}[
]
}
AnnotationTypeElementModifierAnnotationElementModifierプロダクションにより、注釈型宣言内のメソッド宣言をdefault
またはstatic
にすることはできません。したがって、注釈型インタフェースでは、標準インタフェース型と同じ種類のメソッドを宣言できません。注釈型インタフェースがその暗黙的なスーパーインタフェースjava.lang.annotation.Annotation
からデフォルト・メソッドを継承することも考えられますが、Java SE 14の時点ではそのようなデフォルト・メソッドは存在しません。
規則により、注釈
型要素宣言に存在する必要がある唯一のAnnotationTypeElementModifierAnnotationElementModifierは注釈です。
注釈型インタフェースで宣言されるメソッドの戻り型は、次のいずれかである必要があります。そうでない場合はコンパイル時にエラーが発生します。
このルールによって、次のようなネストした配列型を持つ要素を防ぐことができます。
@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 注釈型要素のデフォルト
注釈型要素宣言には、キーワードdefault
とElementValueを含む要素の(空の)パラメータ・リストに従って指定されるデフォルト値を含めることができます(9.7.1)。
- DefaultValue:
default
ElementValue
要素の型が、指定されたデフォルト値に対応していない(9.7)場合、コンパイル時にエラーが発生します。
デフォルト値は注釈にコンパイルされず、注釈が読み取られるときに動的に適用されます。したがって、デフォルト値を変更すると、変更が加えられる前にコンパイルされたクラスの注釈にも影響します(これらの注釈に、デフォルトが指定された要素の明示的な値がないものと仮定します)。
例9.6.2-1.デフォルト値を含む注釈型宣言
9.6.1のRequestForEnhancement
注釈型インタフェースを改良したものを次に示します。
@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の包含注釈型インタフェースです。
TCACによって、戻り型がTA[]
[]
であるvalue()
メソッド要素が宣言されます。TCACによって宣言された、value()
以外のメソッド要素にデフォルト値があります。少なくとも
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.SOURCE
、java.lang.annotation.RetentionPolicy.CLASS
またはjava.lang.annotation.RetentionPolicy.RUNTIME
です。
少なくとも
TCACと同じ種類のプログラム要素にTAを適用できます(9.6.4.1)。具体的には、TAを適用できるプログラム要素の種類がセットm1によって示され、TCACを適用できるプログラム要素の種類がセットm2によって示される場合、m2内のそれぞれの種類がm1に出現する必要があります。ただし、次の場合は除きます。m2内の種類が
java.lang.annotation.ElementType.ANNOTATION_TYPE
である場合は、java.lang.annotation.ElementType.ANNOTATION_TYPE
、java.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に出現する必要があります。
この条項によって、注釈
型インタフェースは、それを適用できるプログラム要素の種類の一部でのみ繰返し可能にすることができるというポリシーが実装されます。java.lang.annotation.Documented
に対応する(メタ)注釈がTAの宣言に含まれている場合、java.lang.annotation.Documented
に対応する(メタ)注釈がTCACの宣言に含まれている必要があります。TAが@Documented
でないときにTCACが@Documented
であることは許容されます。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
をその包含注釈型インタフェースとして指定しようとしていますが、実際は、FooContainer
はFoo
の包含注釈型インタフェースではないため、Foo
宣言をコンパイルすると、コンパイル時にエラーが生成されます。(FooContainer.value()
の戻り型はFoo[]
ではありません。)
@Repeatable
注釈を繰り返すことはできないため、繰返し可能な注釈型インタフェースで指定できる包含注釈型インタフェースは1つのみです。
複数の包含注釈
型インタフェースの指定を許可すると、繰返し可能な注釈型インタフェースの複数の注釈が論理的にコンテナ注釈(9.7.5)に置き換えられるときに、コンパイル時に望ましくない選択が行われます。
注釈型インタフェースは、最大1つの注釈型インタフェースの包含注釈型インタフェースにすることができます。
このことは、注釈
型TインタフェースAの宣言でTCACという包含注釈型インタフェースを指定する場合、TCACのvalue()
メソッドの戻り型はTAを含む、具体的にはTA[]
[]
であるという要件によって暗黙的に規定されています。
注釈型インタフェースでそれ自体をその包含注釈型インタフェースとして指定することはできません。
このことは、包含注釈
型インタフェースのvalue()
メソッドに関する要件によって暗黙的に規定されています。具体的には、注釈型インタフェースAでそれ自体を(@Repeatable
によって)その包含注釈型インタフェースとして指定する場合、Aのvalue()
メソッドの戻り型は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
がその包含注釈型インタフェースである場合を考えてみましょう。
Foo
に@Target
メタ注釈がなく、FooContainer
にも@Target
メタ注釈がない場合、@Foo
は、注釈をサポートするすべてのプログラム要素で繰り返すことができます。Foo
には@Target
メタ注釈がないが、FooContainer
には@Target
メタ注釈がある場合、@Foo
は、@FooContainer
が出現できるプログラム要素でのみ繰り返すことができます。Foo
に@Target
メタ注釈がある場合、Javaプログラミング言語の設計者の判断で、Foo
の適用可能性を理解したうえでFooContainer
を宣言する必要があります。具体的には、FooContainer
が出現できるプログラム要素の種類は、論理的にFoo
の種類と同じであるか、そのサブセットである必要があります。たとえば、
Foo
がフィールド宣言とメソッド宣言に適用可能である場合、FooContainer
がフィールド宣言にのみ適用可能である(@Foo
をメソッド宣言で繰り返すことができない)とすると、FooContainer
はFoo
の包含注釈型インタフェースとして正当に機能できます。しかし、FooContainer
が仮パラメータ宣言にのみ適用可能であるとすると、@Foo
が繰り返される一部のプログラム要素で@FooContainer
を暗黙的に宣言できないため、FooContainer
は、Foo
による包含注釈型インタフェースの選択肢として適切ではありません。同様に、
Foo
がフィールド宣言とメソッド宣言に適用可能である場合、FooContainer
がフィールド宣言とパラメータ宣言に適用可能であるとすると、FooContainer
はFoo
の包含注釈型インタフェースとして正当に機能できません。プログラム要素の交差を使用して、フィールド宣言でのみFoo
を繰返し可能にすることもできますが、FooContainer
に他のプログラム要素が存在することは、FooContainer
がFoo
の包含注釈型インタフェースとして設計されていないことを示します。したがって、Foo
がそれに依存することは危険です。
例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定数に対応しています。
モジュール宣言(7.7)
java.lang.annotation.ElementType.MODULE
に対応していますパッケージ宣言(7.4.1)
java.lang.annotation.ElementType.PACKAGE
に対応しています型宣言: クラス、インタフェース、enumおよび注釈
型宣言(8.1.1、9.1.1、8.5、9.5、8.9、9.6)java.lang.annotation.ElementType.TYPE
に対応していますまた、注釈
型宣言はjava.lang.annotation.ElementType.ANNOTATION_TYPE
に対応していますメソッド宣言(注釈
型インタフェースの要素を含む) (8.4.3、9.4、9.6.1)java.lang.annotation.ElementType.METHOD
に対応していますコンストラクタ宣言(8.8.3)
java.lang.annotation.ElementType.CONSTRUCTOR
に対応しています汎用クラス、インタフェース、メソッドおよびコンストラクタの型パラメータ宣言(8.1.2、9.1.2、8.4.4、8.8.4)
java.lang.annotation.ElementType.TYPE_PARAMETER
に対応していますフィールド宣言(enum定数を含む) (8.3.1、9.3、8.9.1)
java.lang.annotation.ElementType.FIELD
に対応しています仮パラメータおよび例外パラメータ宣言(8.4.1、9.4、14.20)
java.lang.annotation.ElementType.PARAMETER
に対応していますローカル変数宣言(
for
文のループ変数およびtry
-with-resources文のリソース変数を含む) (14.4、14.14.1、14.14.2、14.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
に対応する(メタ)注釈mがTに含まれている場合、次のようになります。
値が
java.lang.annotation.RetentionPolicy.SOURCE
である要素をmが持つ場合、Javaコンパイラは、aが出現するクラスまたはインタフェースのバイナリ表現にaが存在しないことを確認する必要があります。値が
java.lang.annotation.RetentionPolicy.CLASS
またはjava.lang.annotation.RetentionPolicy.RUNTIME
である要素をmが持つ場合、Javaコンパイラは、aが出現するクラスまたはインタフェースのバイナリ表現でaが表されていることを確認する必要があります。ただし、aがローカル変数宣言に注釈を付ける場合、またはaがラムダ式の仮パラメータ宣言に注釈を付ける場合は除きます。ローカル変数の宣言、またはラムダ式の仮パラメータの宣言にある注釈がバイナリ表現で保持されることはありません。一方、注釈
型インタフェースで適切な保持ポリシーが指定されている場合、ローカル変数の型、またはラムダ式の仮パラメータの型にある注釈はバイナリ表現で保持されます。注釈
型インタフェースに@Target(java.lang.annotation.ElementType.LOCAL_VARIABLE)
および@Retention(java.lang.annotation.RetentionPolicy.CLASS)
または@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
をメタ注釈として付けることは不正ではありません。値が
java.lang.annotation.RetentionPolicy.RUNTIME
である要素をmが持つ場合、Java SEプラットフォームのリフレクション・ライブラリは、aを実行時に使用できるようにする必要があります。
java.lang.annotation.Retention
に対応する(メタ)注釈mがTに含まれていない場合、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) { ... }
これは完全に正当ですが、クラス
Foo
はObject
からequals
実装を継承するため、微妙なバグの原因となる可能性があります。
型クラスまたはインタフェースTのメソッド宣言に@Override
の注釈が付けられているが、そのメソッドが、Tのスーパー・タイプで宣言されたメソッドをTからオーバーライドしない場合(8.4.8.1、9.4.1.1)、またはObject
のpublic
メソッドとオーバーライド等価でない場合(4.3.2、8.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.hashCode
をFoo
からオーバーライドするため、クラス宣言での@Override
の使用は、最初の条項によって正当です。インタフェース宣言については、インタフェースが
Object
をスーパー・タイプとして持たず、Object
のpublic
メンバーに対応するpublic
abstract
メンバーをインタフェースが持つとします(9.2)。インタフェースでそれらを明示的に宣言する(つまり、Object
のpublic
メソッドとオーバーライド等価であるメンバーを宣言する)ことを選択した場合、インタフェースはそれらをオーバーライドするとみなされ、@Override
の使用は許可されます。一方、
clone
メソッドで@Override
を使用しようとするインタフェースを考えてみましょう。(この例では、finalize
を使用することもできます)interface Quux { @Override Object clone(); }
Object.clone
はpublic
でないため、Quux
で暗黙的に宣言されたclone
というメンバーはありません。したがって、Quux
内のclone
の明示的な宣言は他のメソッドを実装するとみなされず、@Override
の使用は誤りです。(Quux.clone
はpublic
であるという事実は関係ありません。)対照的に、
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種類の警告が定義されています。
未チェック警告(4.8、5.1.6、5.1.9、8.4.1、8.4.8.3、15.12.4.2、15.13.2、15.27.3)は、文字列"
unchecked
"によって指定されます。非推奨警告(9.6.4.6)は、文字列"
deprecation
"によって指定されます。削除警告(9.6.4.6)は、文字列"
removal
"によって指定されます。プレビュー警告(1.5)は、文字列"
preview
"によって指定されます。
他の文字列は、標準以外の警告を指定します。Javaコンパイラは、認識しないこのような文字列を無視する必要があります。
コンパイラ・ベンダーは、
@SuppressWarnings
についてサポートする文字列を文書化することと、同じ文字列が複数のコンパイラにわたって認識されるようにするために協力し合うことを奨励されています。
9.6.4.6 @Deprecated
プログラマは、特定のプログラム要素(モジュール、型クラス、インタフェース、フィールド、メソッドおよびコンストラクタ)について、それらが危険であったり、より効果的な代替手段が存在するために使用を推奨されないことがあります。注釈型インタフェースDeprecated
を使用すると、そうしたプログラム要素の使用についてコンパイラで警告できます。
非推奨のプログラム要素は、宣言に@Deprecated
の注釈が付けられているモジュール、型クラス、インタフェース、フィールド、メソッドまたはコンストラクタです。プログラム要素の非推奨の形態は、注釈のforRemoval
要素の値によって異なります。
forRemoval=false
(デフォルト)の場合、プログラム要素は通常の非推奨です。通常の非推奨プログラム要素は、将来のリリースで削除されるものではありませんが、それでも、プログラマはその使用から移行する必要があります。
forRemoval=true
の場合、プログラム要素は廃止予定の非推奨です。廃止予定の非推奨プログラム要素は、将来のリリースで削除されるものです。プログラマは、その使用を中止する必要があります。そうしないと、新しいリリースにアップグレードするときに、ソースおよびバイナリの非互換性(13.2)のリスクがあります。
プログラム要素の宣言で(明示的に宣言されているか、暗黙的に宣言されているかに関係なく)通常の非推奨プログラム要素が使用されている(オーバーライドされている、呼び出されている、または名前で参照されている)場合、Javaコンパイラは非推奨警告を生成する必要があります。ただし、次の場合は除きます。
それ自体が非推奨(通常または廃止予定)である宣言内で使用されています。あるいは
非推奨警告を抑制するように注釈が付けられている(9.6.4.5)宣言内で使用されています。あるいは
使用が出現する宣言と通常の非推奨プログラム要素の宣言の両方が同じ最も外側のクラス内にあります。あるいは
通常の非推奨型またはメンバーをインポートする
import
宣言内で使用されています。あるいはexports
またはopens
ディレクティブ(7.7.2)内で使用されています。
プログラム要素の宣言で(明示的に宣言されているか、暗黙的に宣言されているかに関係なく)廃止予定の非推奨プログラム要素が使用されている(オーバーライドされている、呼び出されている、または名前で参照されている)場合、Javaコンパイラは削除警告を生成する必要があります。ただし、次の場合は除きます。
削除警告を抑制するように注釈が付けられている(9.6.4.5)宣言内で使用されています。あるいは
使用が出現する宣言と廃止予定の非推奨プログラム要素の宣言の両方が同じ最も外側のクラス内にあります。あるいは
廃止予定の非推奨
型クラス、インタフェースまたはメンバーをインポートするimport
宣言内で使用されています。あるいはexports
またはopens
ディレクティブ内で使用されています。
廃止予定の非推奨には十分な緊急性があるため、廃止予定の非推奨要素が使用されていると、使用している側の要素自体が非推奨であっても、削除警告が生成されます。両方の要素が同時に削除されるという保証がないためです。警告を閉じて要素の使用を続行するには、プログラマは、
@SuppressWarnings
注釈を使用して、そのリスクを手動で確認する必要があります。
次の場合、非推奨警告または削除警告は生成されません。
ローカル変数または仮パラメータが使用されています(名前で参照されています)。ローカル変数または仮パラメータの宣言に
@Deprecated
の注釈が付けられている場合も含みます。パッケージの名前が使用されています(修飾された型名、
import
宣言、あるいはexports
またはopens
ディレクティブによって参照されています)。パッケージの宣言に@Deprecated
の注釈が付けられている場合も含みます。モジュールの名前が、修飾された
exports
またはopens
ディレクティブによって使用されています。フレンド・モジュールの宣言に@Deprecated
の注釈が付けられている場合も含みます。
パッケージをエクスポートする、または開くモジュール宣言は、通常、パッケージの宣言を制御するのと同じプログラマまたはチームによって制御されます。そのため、パッケージがモジュール宣言によってエクスポートされたり、開かれたときに、パッケージ宣言に
@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
メタ注釈は、TCをTの包含注釈型インタフェースにするには十分ではありません。TCがTの包含注釈型インタフェースとみなされるための様々な整形式ルールがあります。
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が配列型E
[]
で、かつ次のいずれかが当てはまる。VがConditionalExpressionまたはAnnotationである場合に、VがEに対応している。
VがElementValueArrayInitializerである場合に、Vに含まれる各要素値がEに対応している。
ElementValueArrayInitializerには構文的に注釈、式およびネストされたイニシャライザを含めることができることを除いて、ElementValueArrayInitializerは標準配列イニシャライザ([10.6])と同様です。ただし、ElementValueArrayInitializerでは、ネストされたイニシャライザは、注釈
型インタフェース宣言の配列型要素に対応することはない(ネストされた配列型は許可されない)ため、意味的に正当ではありません。
Tが配列型でなく、Vの型がTと割当て互換([5.2])し、かつ:
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.1のPreliminary
マーカー注釈型インタフェースを使用した例を示します。
@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)に適用できます。たとえば、
A
、B
および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つの宣言コンテキストのいずれかで考えられます。
メソッド宣言(注釈
型インタフェースの要素を含む)コンストラクタ宣言
フィールド宣言(enum定数を含む)
仮パラメータ宣言および例外パラメータ宣言
ローカル変数宣言(
for
文のループ変数およびtry
-with-resources文のリソース変数を含む)
Javaプログラミング言語の文法では、これらの場所にある注釈が宣言(8.3)の修飾子として明確に取り扱われますが、これは純粋に構文上だけのものです。注釈が宣言または宣言されたエンティティの型のどちらに適用されるか、つまり、注釈が宣言注釈または型注釈のどちらであるかは、注釈の型インタフェースの適用性によって決まります。
注釈の
型インタフェースが、宣言に対応する宣言コンテキストに適用可能であり、型コンテキストには適用可能でない場合、注釈は宣言のみに適用されるとみなされます。注釈の
型インタフェースが、型コンテキストに適用可能であり、宣言に対応する宣言コンテキストには適用可能でない場合、注釈は、その注釈に最も近い型のみに適用されるとみなされます。注釈の
型インタフェースが、宣言に対応する宣言コンテキストに適用可能であると同時に型コンテキストに適用可能である場合、注釈は、宣言とその注釈に最も近い型の両方に適用されるとみなされます。
前述の2番目のと3番目のケースでは、注釈に最も近い型は、次のように決定されます。
注釈が
void
メソッド宣言、またはvar
を使用するローカル変数宣言(14.4、14.14.2、14.20.3)の前にある場合、最も近い型は存在しません。注釈の型インタフェースが、その注釈に最も近い型のみに適用されるとみなされる場合、コンパイル時にエラーが発生します。注釈がコンストラクタ宣言の前にある場合、最も近い型は、新しく構築されたオブジェクトの型です。新しく構築されたオブジェクトの型は、コンストラクタ宣言の直前と直後にある型の完全修飾名です。この完全修飾名内で、注釈は、コンストラクタ宣言が示す単純な型名に適用されます。
その他すべてのケースでは、最も近い型は、宣言されたエンティティのソース・コード内に書かれた型です。その型が配列型である場合、その要素型が注釈に最も近いとみなされます。
たとえば、フィールド宣言
@Foo public static String f;
の場合、@Foo
に最も近い型はString
です。(フィールド宣言の型がjava.lang.String
として作成されていた場合、java.lang.String
が、@Foo
に最も近い型になり、後のルールにより、型注釈をパッケージ名java
に適用することが禁止されます。)汎用メソッド宣言@Foo <T> int[] m() {...}
の場合、宣言されたエンティティ用として作成された型はint[]
であるため、@Foo
は要素型int
に適用されます。var
を使用しないローカル変数宣言と、ラムダ式の仮パラメータ宣言は、両方とも、ソース・コードで宣言注釈と型注釈を使用可能であるが、型注釈のみをclass
ファイルに格納できるという点が似ています。
型TインタフェースAの注釈が次の構文上の修飾子である場合、コンパイル時にエラーが発生します。
モジュール宣言: ただし、
TAはモジュール宣言には適用できません。パッケージ宣言: ただし、
TAはパッケージ宣言には適用できません。クラス
、またはインタフェースまたはenum宣言: ただし、TAは型宣言にも型コンテキストにも適用できません。または注釈型宣言の場合:ただし、TAは注釈型宣言にも、型宣言にも、型コンテキストにも適用できません。メソッド宣言(注釈
型インタフェースの要素を含む): ただし、TAはメソッド宣言または型コンテキストに適用できません。コンストラクタ宣言: ただし、
TAはコンストラクタ宣言または型コンテキストに適用できません。汎用クラス、インタフェース、メソッドまたはコンストラクタの型パラメータ宣言: ただし、
TAは型パラメータ宣言または型コンテキストに適用できません。フィールド宣言
(またはenum定数を含む): ただし、TAはフィールド宣言または型コンテキストに適用できません。仮パラメータ宣言または例外パラメータ宣言: ただし、
TAは仮パラメータ宣言および例外パラメータ宣言または型コンテキストに適用できません。レシーバ・パラメータ: ただし、
TAは型コンテキストに適用できません。ローカル変数宣言(
for
文のループ変数またはtry
-with-resources文のリソース変数を含む): ただし、TAはローカル変数宣言または型コンテキストに適用できません。
これら9つの条項のうち5つの条項で「...または型コンテキスト」と記載されているのは、これらが、注釈を宣言または宣言されたエンティティの型に適用できるように見える5つの構文上の場所を特徴付けているからです。また、クラス
、およびインタフェース、enumおよび注釈型宣言、さらに型パラメータ宣言に関する9つの条項のうち2つの条項で「...または型コンテキスト」と記載されているのは、型インタフェースに@Target(ElementType.TYPE_USE)
がメタ注釈として付けられている(ため、型コンテキストに適用可能である)注釈を型クラス、インタフェースまたは型パラメータ宣言に適用する方が便利である可能性があるからです。
次の両方が当てはまる場合、型注釈は許容されます。
注釈が最も近い単純名がPackageNameではなくTypeNameとして分類されている。
注釈が最も近い単純名の後ろに「
.
」および別のTypeNameがある。つまり、注釈が@Foo T.U
として出現し、U
がT
の内部クラスを示している。
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)、TAとTAの包含注釈型インタフェースの両方が宣言コンテキストまたは型コンテキストに適用可能である場合は除きます(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つのみの場合、FooContainer
がFoo
の包含注釈型インタフェースである場合でも、暗黙的に宣言されるコンテナ注釈はありません。ただし、次のような型インタフェース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
オブジェクトを構築できます。
正常に動作するクラス・ローダーは、次の特性を有しています。
同じ名前が与えられた場合、適切なクラス・ローダーは常に同じクラス・オブジェクトを返します。
クラス・ローダーL1がクラスCのロードを別のローダーL2に委譲する場合、Cの直接スーパークラス型または直接スーパーインタフェース型
として出現するによって名前が付けられるか、Cのフィールドの型として出現するによって名前が付けられるか、Cのメソッドまたはコンストラクタの仮パラメータの型として出現するによって名前が付けられるか、Cのメソッドの戻り型として出現するによって名前が付けられる型TクラスまたはインタフェースDについては、L1とL2が同じ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プログラミング言語で作成されたコードによっても、そのようなその他の機密性の高いシステム・クラス(
ClassLoader
やSecurityManager
など)の実装によっても、実行時の型システムを覆すことはできません。
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
ファイルには、特定の特性が必要です。これらの特性の多くは、バイナリの互換性を確保するソース・コードの変換をサポートするように明確に選択されています。必須特性は次のとおりです。
クラスまたはインタフェースはバイナリ名によって名前を付ける必要があり、これは次の制約を満たす必要があります。
メンバー
型・クラスまたはインタフェース(8.5、9.5)のバイナリ名は、それを直接包含する型クラスまたはインタフェースのバイナリ名、その後に続く$
、その後に続くメンバーの単純名で構成されます。ローカル・クラス(14.3)のバイナリ名は、それを直接包含する
型クラスまたはインタフェースのバイナリ名、その後に続く$
、その後に続く空でない一連の数字、その後に続くローカル・クラスの単純名で構成されます。無名クラス(15.9.5)のバイナリ名は、それを直接包含する
型クラスまたはインタフェースのバイナリ名、その後に続く$
、その後に続く空でない一連の数字で構成されます。汎用クラスまたはインタフェース(8.1.2、9.1.2)によって宣言される型変数のバイナリ名は、それを直接包含する
型クラスまたはインタフェースのバイナリ名、その後に続く$
、その後に続く型変数の単純名で構成されます。汎用メソッド(8.4.4)によって宣言される型変数のバイナリ名は、そのメソッドを宣言する
型クラスまたはインタフェースのバイナリ名、その後に続く$
、その後に続くメソッドの記述子(JVMS §4.3.3)、その後に続く$
、その後に続く型変数の単純名で構成されます。汎用コンストラクタ(8.8.4)によって宣言される型変数のバイナリ名は、そのコンストラクタを宣言する
型クラスのバイナリ名、その後に続く$
、その後に続くコンストラクタの記述子(JVMS §4.3.3)、その後に続く$
、その後に続く型変数の単純名で構成されます。
型変数にバイナリ名が必要な理由は不明です。
別のクラスまたはインタフェース
型への参照は、型クラスまたはインタフェースのバイナリ名を使用するシンボリックである必要があります。定数変数(4.12.4)であるフィールドへの参照は、コンパイル時に定数変数のイニシャライザが示す値Vに解決される必要があります。
このようなフィールドが
static
である場合、バイナリ・ファイルのコード内にフィールドへの参照が存在しない必要があります。これには、このフィールドを宣言したクラスまたはインタフェースも含まれます。このようなフィールドは常に、初期化(12.4.2)されているものとして示される必要があり、このフィールドのデフォルトの初期値は(Vとは異なる場合)、表示されないようにします。このようなフィールドが非
static
である場合、バイナリ・ファイルのコード内にフィールドへの参照が存在しない必要があります。ただし、このフィールドが含まれるクラス内は除きます。(インタフェースにはstatic
フィールドしかないため、これはインタフェースではなくクラスです。)このクラスには、インスタンス作成時(12.5)にフィールドの値をVに設定するためのコードが必要です。クラスC内でのフィールド・アクセスを示す正当な式が与えられ、定数値ではなく、(場合によっては別個の)クラスまたはインタフェースD内で宣言されているfという名前のフィールドを参照している場合、Oracleではフィールド参照の修飾
型クラスまたはインタフェースを次のように定義します。式が単純名によって参照される場合に、fが現在のクラスまたはインタフェースCのメンバーである場合、
TQはCであるとします。それ以外の場合、TQは、fがメンバーである、最も内側の字句的な包含型クラスまたはインタフェース宣言であるとします。どちらの場合も、TQが参照の修飾型クラスまたはインタフェースです。参照の形式がTypeName
.
fであり、TypeNameがクラスまたはインタフェースを示している場合、TypeNameが示すクラスまたはインタフェースが参照の修飾型クラスまたはインタフェースです。式の形式がExpressionName
.
fまたはPrimary.
fである場合は次のようになります。式の形式が
super.
fの場合、Cのスーパークラスは、参照の修飾型クラスまたはインタフェースです。式の形式がTypeName
.super.
fの場合、TypeNameで示されるクラスのスーパークラスは、参照の修飾型クラスまたはインタフェースです。
fへの参照は、参照の修飾
型クラスまたはインタフェースのイレイジャ(4.6)およびフィールドの単純名fへのシンボリック参照にコンパイルする必要があります。また、型が想定どおりであるかどうかを検証者がチェックできるように、この参照には、宣言されたフィールドの型のイレイジャへのシンボリック参照も含まれている必要があります。クラスまたはインタフェースC内のメソッド呼出し式またはメソッド参照式が与えられ、(場合によっては別個の)クラスまたはインタフェースD内で宣言されている(または暗黙的な宣言されている(9.2)) mという名前のメソッドを参照している場合、Oracleではメソッド呼出しの修飾
型クラスまたはインタフェースを次のように定義します。Dが
Object
である場合、式の修飾型クラスまたはインタフェースはObject
です。そうでない場合は、次のようになります。
メソッドが単純名によって参照されているときに、mが現在のクラスまたはインタフェースCのメンバーである場合、
TQをCにします。そうでない場合、TQは、mがメンバーである、最も内側にあり字句的な包含型クラスまたはインタフェース宣言にします。どちらの場合も、TQがメソッド呼出しの修飾型クラスまたはインタフェースです。式の形式がTypeName
.
mまたはReferenceType::
mの場合、TypeName (またはReferenceTypeのイレイジャ(4.6))で示される型クラスまたはインタフェースは、メソッド呼出しの修飾型クラスまたはインタフェースです。式の形式がExpressionName
.
m、Primary.
m、ExpressionName::
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
を宣言され、値を戻さないことを示すものが含まれている必要があります。クラス・インスタンス作成式(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)へのシンボリック参照に解決される必要があります。コンストラクタのシグネチャには、次の両方が含まれている必要があります。コンストラクタのパラメータ数
各仮パラメータの型へのシンボリック参照
クラスまたはインタフェースのバイナリ表現には、次もすべて含まれる必要があります。
これがクラスであり、
Object
ではない場合、このクラスの直接スーパークラスのイレイジャへのシンボリック参照。それぞれの直接スーパーインタフェース
のイレイジャへのシンボリック参照(存在する場合)。フィールドの単純名およびフィールドの型のイレイジャへのシンボリック参照として与えられた、クラスまたはインタフェース内で宣言された各フィールドの仕様。
これがクラスである場合、各コンストラクタの消去されたシグネチャ(前述を参照)。
クラスまたはインタフェース内で宣言された各メソッド(インタフェースについては、暗黙的に宣言されたメソッドを除く(9.2))ごとに、消去されたシグネチャおよび戻り型(前述を参照)。
クラスまたはインタフェースを実装するために必要なコード。
それぞれの
型クラスまたはインタフェースに、その正規名(6.7)を回復するための十分な情報が含まれている必要があります。それぞれのメンバー
型・クラスまたはインタフェースに、そのソースレベルのアクセス修飾子を回復するための十分な情報が含まれている必要があります。それぞれのネストしたクラス
およびネストしたまたはインタフェースに、それを直接包含する型クラスまたはインタフェース(8.1.3)へのシンボリック参照が含まれている必要があります。それぞれのクラスまたはインタフェースに、そのメンバー
型・クラスおよびインタフェース(8.5、9.5)すべてへのシンボリック参照と、そのメソッド、コンストラクタ、静的イニシャライザ、インスタンス・イニシャライザおよびフィールド・イニシャライザに出現するローカル・クラスと無名クラスその本体内で宣言された他のネストしたクラスおよびインタフェースすべてへのシンボリック参照が含まれている必要があります。すべてのインタフェースに、すべてのメンバー型(9.5)へのシンボリック参照、およびデフォルトのメソッドとフィールド・イニシャライザ内に出現するローカル・クラスと無名クラスへのシンボリック参照が含まれている必要があります。Javaコンパイラによって発行されたコンストラクトは、発行されたコンストラクトがクラス初期化メソッドでないかぎり、ソース・コードで明示的または暗黙的に宣言されたコンストラクトに対応していない場合、合成としてマークする必要があります(JVMS §2.9)。
Javaコンパイラによって発行されたコンストラクトは、ソース・コードで暗黙的に宣言された仮パラメータに対応している場合、必須としてマークする必要があります(8.8.1、8.8.9、8.9.3、15.9.5.1)。
次の仮パラメータは、ソース・コードで暗黙的に宣言されます。
参考までに、次のコンストラクタは、ソース・コードでは暗黙的に宣言されますが、
class
ファイル内で必須としてマークできるのは仮パラメータのみであるため、必須としてはマークされません(JVMS §4.7.24)。
モジュール宣言に対応するclass
ファイルには、バイナリ名がmodule-info
であり、スーパークラス、スーパーインタフェース、フィールドおよびメソッドを持たないクラスのclass
ファイルのプロパティが必要です。また、このモジュールのバイナリ表現には次のものがすべて含まれている必要があります。
module
の後ろに示す名前へのシンボリック参照として与えられたモジュールの名前の仕様。また、仕様では、モジュールが標準とオープンのどちらであるかも示す必要があります(7.7)。requires
ディレクティブが示すモジュールの名前へのシンボリック参照として与えられ、このディレクティブによって示される各依存の仕様(7.7.1)。また、仕様では、依存がtransitive
であるかどうか、および依存がstatic
であるかどうかも示す必要があります。exports
またはopens
ディレクティブが示すパッケージの名前へのシンボリック参照として与えられ、これらのディレクティブが示す各パッケージの仕様(7.7.2)。また、ディレクティブが修飾されていた場合、仕様では、ディレクティブのto
句が示すモジュールの名前へのシンボリック参照を与える必要があります。uses
ディレクティブによって示される型クラスまたはインタフェースの名前へのシンボリック参照として指定される、このディレクティブが示す各サービスの仕様(7.7.3)。provides
ディレクティブのwith
句によって示される型クラスおよびインタフェースの名前へのシンボリック参照として指定される、このディレクティブが示すサービス・プロバイダの仕様(7.7.4)。また、この仕様では、このディレクティブによってサービスとして示される型クラスまたはインタフェースの名前へのシンボリック参照を指定する必要があります。
次の各セクションでは、既存のバイナリとの互換性を損なわずにクラスおよびインタフェース型宣言に加えることができる変更について説明します。前述の翻訳要件に基づき、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
がスローされます。このようにベリファイアが異議を申し立てるのは、Super
がHyper
のサブクラスではないため、new Super()
の結果を型Hyper
の仮パラメータのかわりに引数として渡すことができないからです。
検証ステップがなければどのようになる可能性があるかについて検討することをお薦めします。この場合、プログラムを実行すると、次のように出力される可能性があります。
s
これは、ベリファイアがなければ、互換性のないバイナリ・ファイルをリンクすると、これらの各ファイルが正しいJavaコンパイラによって生成されたものであったとしても、Java型システムが無効になる可能性があることを示しています。
ここから得られる教訓は、ベリファイアがない実装やベリファイアを使用できない実装の場合、型の安全性が維持されないため、有効な実装ではないということです。
マルチ
catch
句(14.20)の選択肢が相互のサブクラスまたはスーパークラスであってはならないという要件は、ソースの制限にすぎません。次のクライアント・コードが正当であるとします。try { throwAorB(); } catch(ExceptionA | ExceptionB e) { ... }
ここで、クライアントのコンパイル時に
ExceptionA
とExceptionB
がサブクラス/スーパークラス関係を持たない場合、クライアントの実行時にExceptionA
とExceptionB
のクライアントがこのような関係を持つことに関してはバイナリ互換性があります。このことは、クライアントに対してバイナリ互換性があるクラス変換が同じクライアントに対してソース互換性がないような他の状況に類似しています。
13.4.8 フィールド宣言
広範に配布されるプログラムがフィールドをそのクライアントに公開しないようにしてください。これは、前述のバイナリ互換性の問題とは別の、一般的に推奨されるソフトウェア・エンジニアリングの慣例です。クラスにフィールドを追加すると、再コンパイルされていない既存のバイナリとの互換性が損なわれる可能性があります。
修飾型TクラスCを持つフィールドfへの参照があるとします。さらに、fは実際には、TCのスーパークラスSで宣言されたインスタンス(それぞれstatic
)フィールドであり、fの型はXであるとします。
fと同じ名前の型Xの新しいフィールドが、TCのスーパークラスSのサブクラスまたはTC自体に追加される場合、リンク・エラーが発生する可能性があります。そのようなリンク・エラーは、前述以外では、次のいずれかが当てはまる場合のみ発生します。
新しいフィールドが、古いフィールドよりもアクセス可能性が低い。
新しいフィールドが、
static
(それぞれインスタンス)フィールドである。
特に、フィールド・アクセスが、互換性のない型を持つスーパークラスのフィールドを以前に参照していたため、クラスを再コンパイルできなくなった場合は、リンク・エラーは発生しません。そのような参照がある、以前にコンパイルされたクラスは、引き続きスーパークラスで宣言されたフィールドを参照します。
...
13.4.12 メソッドおよびコンストラクタ宣言
メソッドまたはコンストラクタ宣言をクラスに追加した場合は、呼出しが、互換性のない型を持つスーパークラスのメソッドまたはコンストラクタを以前に参照していたため、型クラスを再コンパイルできなくなった場合でも、既存のバイナリとの互換性が損なわれることはありません。そのような参照がある、以前にコンパイルされたクラスは、引き続きスーパークラスで宣言されたメソッドまたはコンストラクタを参照します。
修飾型TクラスCを持つメソッドmへの参照があるとします。さらに、mは実際には、TCのスーパークラスSで宣言されたインスタンス(それぞれstatic
)メソッドであるとします。
mと同じシグネチャおよび戻り型を持つ型Xの新しいメソッドが、TCのスーパークラスSのサブクラスまたはTC自体に追加される場合、リンク・エラーが発生する可能性があります。そのようなリンク・エラーは、前述以外では、次のいずれかが当てはまる場合のみ発生します。
新しいメソッドが、古いフィールドよりもアクセス可能性が低い。
新しいメソッドが、
static
(それぞれインスタンス)メソッドである。
...
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)。
Cは、this
の型は、中にキーワードthis
が出現するクラスまたはインタフェース型Tです。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
という形式の式の値は、this
のn番目の字句的な包含インスタンスです。
TypeNameが汎用であり、型パラメータF1, ..., Fnを持つ場合、式の型はTypeName<F1, ..., Fn>です。それ以外の場合、式の型はCTypeNameです。
TypeNameが式の字句的な包含クラスまたはインタフェース宣言ではない場合、あるいはクラスTTypeNameまたはTTypeName自体の内部クラスではないクラスまたはインタフェースに式が出現する場合、コンパイル時にエラーが発生します。
15.9 クラス・インスタンス作成式
15.9.1 インスタンス化対象のクラスの確認
ClassOrInterfaceTypeToInstantiateが(<>
ではなく)TypeArgumentsで終わる場合、ClassOrInterfaceTypeToInstantiateが整形式のパラメータ化された型(4.5)を表す必要があります。そうでない場合、コンパイル時にエラーが発生します。
ClassOrInterfaceTypeToInstantiateが<>
で終わるが、ClassOrInterfaceTypeToInstantiate内のIdentifierによって表される型クラスまたはインタフェースが汎用ではない場合、コンパイル時にエラーが発生します。
クラス・インスタンス作成式がクラス本体で終わる場合、インスタンス化対象のクラスは匿名クラスです。次に、
クラス・インスタンス作成式が修飾されていない場合、次のようになります。
ClassOrInterfaceTypeToInstantiate内のIdentifierは、アクセス可能であり、非
final
であり、enum型クラスではないクラス、またはアクセス可能なインタフェース(6.6)のいずれかを表す必要があります。そうでない場合、コンパイル時にエラーが発生します。ClassOrInterfaceTypeToInstantiate内のIdentifierがクラス、Cを表す場合、Cの匿名直接サブクラスが宣言されます。TypeArgumentsが存在する場合、Cは、TypeArgumentsによって与えられる型引数を持ちます。
<>
が存在する場合、Cは、15.9.3で推測されている型引数を持ちます。そうでない場合、Cは型引数を持ちません。サブクラスの本体は、クラス・インスタンス作成式で与えられるClassBodyです。インスタンス化対象のクラスは匿名サブクラスです。ClassOrInterfaceTypeToInstantiate内のIdentifierがインタフェース、Iを表す場合、Iを実装する
Object
の匿名直接サブクラスが宣言されます。TypeArgumentsが存在する場合、Iは、TypeArgumentsによって与えられる型引数を持ちます。<>
が存在する場合、Iは、15.9.3で推測されている型引数を持ちます。そうでない場合、Iは型引数を持ちません。サブクラスの本体は、クラス・インスタンス作成式で与えられるClassBodyです。インスタンス化対象のクラスは匿名サブクラスです。クラス・インスタンス作成式が修飾される場合、次のようになります。
ClassOrInterfaceTypeToInstantiate内のIdentifierは、アクセス可能であり、非
final
であり、enum型クラスではなく、Primary式またはExpressionNameのコンパイル時型のメンバーである内部クラスを一義的に表す必要があります。そうでない場合、コンパイル時にエラーが発生します。ClassOrInterfaceTypeToInstantiate内のIdentifierがクラス、Cを表すようにします。Cの匿名直接サブクラスが宣言されます。TypeArgumentsが存在する場合、Cは、TypeArgumentsによって与えられる型引数を持ちます。
<>
が存在する場合、Cは、15.9.3で推測されている型引数を持ちます。そうでない場合、Cは型引数を持ちません。サブクラスの本体は、クラス・インスタンス作成式で与えられるClassBodyです。インスタンス化対象のクラスは匿名サブクラスです。
クラス・インスタンス作成式が匿名クラスを宣言しない場合、次のようになります。
クラス・インスタンス作成式が修飾されていない場合、次のようになります。
ClassOrInterfaceTypeToInstantiate内のIdentifierは、アクセス可能であり、非
abstract
であり、enum型クラスではないクラスを表す必要があります。そうでない場合、コンパイル時にエラーが発生します。インスタンス化対象のクラスは、ClassOrInterfaceTypeToInstantiate内のIdentifierによって指定されます。TypeArgumentsが存在する場合、クラスは、TypeArgumentsによって与えられる型引数を持ちます。
<>
が存在する場合、クラスは、15.9.3で推測されている型引数を持ちます。そうでない場合、クラスは型引数を持ちません。クラス・インスタンス作成式が修飾される場合、次のようになります。
ClassOrInterfaceTypeToInstantiateは、アクセス可能であり、非
abstract
であり、enum型クラスではなく、Primary式またはExpressionNameのコンパイル時型のメンバーである内部クラスを一義的に表す必要があります。インスタンス化対象のクラスは、ClassOrInterfaceTypeToInstantiate内のIdentifierによって指定されます。TypeArgumentsが存在する場合、クラスは、TypeArgumentsによって与えられる型引数を持ちます。
<>
が存在する場合、クラスは、15.9.3で推測されている型引数を持ちます。そうでない場合、クラスは型引数を持ちません。
15.9.2 包含インスタンスの特定
Cはインスタンス化されるクラス、iは作成されるインスタンスであるとします。Cが内部クラスである場合、iには、直接包含するインスタンス(8.1.3)がある可能性があり、次のように特定されます。
Cが無名クラスである場合は、次のようになります。
クラス・インスタンス作成式が静的コンテキストに出現する場合、iには、直接包含するインスタンスはありません。
そうでない場合、iを直接包含するインスタンスは
this
です。
Cがローカル・クラスである場合は、次のようになります。
Cが静的コンテキストに出現する場合、iには、直接包含するインスタンスはありません。
そうでない場合、クラス・インスタンス作成式が静的コンテキストに出現すると、コンパイル時にエラーが発生します。
そうでない場合、Oは、Cを直接包含するクラスまたはインタフェース宣言であるとします。nは、Oが、クラス・インスタンス作成式が出現するクラスまたはインタフェースのn番目の字句的な包含
型クラスまたはインタフェース宣言であるような整数であるとします。iを直接包含するインスタンスは、
this
のn番目の字句的な包含インスタンスです。
Cが内部メンバー・クラスである場合は、次のようになります。
クラス・インスタンス作成式が修飾されていない場合、次のようになります。
クラス・インスタンス作成式が静的コンテキストに出現する場合、コンパイル時にエラーが発生します。
そうでない場合、Cが、クラス・インスタンス作成式が出現するクラスまたはインタフェースを包含するクラスのメンバーである場合、Oは、Cがメンバーである、直接包含するクラスであるとします。nは、Oが、クラス・インスタンス作成式が出現するクラスまたはインタフェースのn番目の字句的な包含
型クラスまたはインタフェース宣言であるような整数であるとします。iを直接包含するインスタンスは、
this
のn番目の字句的な包含インスタンスです。そうでない場合、コンパイル時にエラーが発生します。
クラス・インスタンス作成式が修飾されている場合、iを直接包含するインスタンスは、Primary式またはExpressionNameの値であるオブジェクトです。
Cが無名クラスで、かつその直接スーパークラスSが内部クラスである場合、iは、Sに関して直接包含するインスタンスを持つ可能性があり、次のように判別されます。
Sがローカル・クラスである場合は、次のようになります。
Sが静的コンテキストに出現する場合、iには、Sに関して直接包含するインスタンスはありません。
そうでない場合、クラス・インスタンス作成式が静的コンテキストに出現すると、コンパイル時にエラーが発生します。
そうでない場合、Oは、Sを直接包含する
型クラスまたはインタフェース宣言であるとします。nは、Oが、クラス・インスタンス作成式が出現するクラスまたはインタフェースのn番目の字句的な包含型クラスまたはインタフェース宣言であるような整数であるとします。Sに関してiを直接包含するインスタンスは、
this
のn番目の字句的な包含インスタンスです。
Sが内部メンバー・クラスである場合は、次のようになります。
クラス・インスタンス作成式が修飾されていない場合、次のようになります。
クラス・インスタンス作成式が静的コンテキストに出現する場合、コンパイル時にエラーが発生します。
そうでない場合、Sが、クラス・インスタンス作成式が出現するクラスまたはインタフェースを包含するクラスのメンバーである場合、Oは、Sがメンバーである、直接包含するクラスであるとします。nは、Oが、クラス・インスタンス作成式が出現するクラスまたはインタフェースのn番目の字句的な包含
型クラスまたはインタフェース宣言であるような整数であるとします。Sに関してiを直接包含するインスタンスは、
this
のn番目の字句的な包含インスタンスです。そうでない場合、コンパイル時にエラーが発生します。
クラス・インスタンス作成式が修飾されている場合、Sに関してiを直接包含するインスタンスは、Primary式またはExpressionNameの値であるオブジェクトです。
15.9.3 コンストラクタとその引数の選択
Cは、インスタンス化対象のクラスであるとします。Cのインスタンスiを作成する場合は、次のルールによって、コンパイル時にCのコンストラクタが選択されます。
まず、コンストラクタ呼出しの実際の引数が決定します。
Cが直接スーパークラスSを持つ匿名クラスである場合、次のようになります。
Sが内部クラスではない場合、またはSが静的コンテキストに出現するローカル・クラスである場合、コンストラクタの引数はクラス・インスタンス作成式の引数リストの引数(ある場合)であり、式に出現する順序となります。
それ以外の場合、コンストラクタの最初の引数は、Sに関してiを直接包含するインスタンス(15.9.2)で、コンストラクタの後続の引数はクラス・インスタンス作成式の引数リストの引数(ある場合)であり、クラス・インスタンス作成式に出現する順序となります。
Cがローカル・クラスまたは
private
内部メンバー・クラスである場合、コンストラクタの引数はクラス・インスタンス作成式の引数リストの引数(ある場合)であり、クラス・インスタンス作成式に出現する順序となります。Cが非
private
内部メンバー・クラスである場合、コンストラクタの最初の引数は、iを直接包含するインスタンス(8.8.1、15.9.2)で、そのコンストラクタの後続の引数はクラス・インスタンス作成式の引数リストの引数(ある場合)であり、クラス・インスタンス作成式に出現する順序となります。それ以外の場合、コンストラクタの引数はクラス・インスタンス作成式の引数リストの引数(ある場合)であり、式に出現する順序となります。
次に、Cのコンストラクタと、対応するthrows
句および戻り型が決定します。
クラス・インスタンス作成式で
<>
を使用しない場合は、次のようになります。Cが匿名クラスではない場合、次のようになります。
Tは、式のクラス型引数が後に続く、Cで示される型であるとします。15.12.2に規定されている、コンストラクタを処理するように変更されたプロセスは、Tのコンストラクタの1つを選択し、その
throws
句を決定するために使用されます。Tに、適用可能かつアクセス可能な(6.6)一意の最も的確なコンストラクタがない場合、コンパイル時にエラーが発生します(メソッド呼出しと同様)。
それ以外の場合、選択されたコンストラクタに対応する戻り型はTです。
Cが無名クラスである場合は、次のようになります。
15.12.2に規定されている、コンストラクタを処理するように変更されたプロセスは、Cの直接スーパークラス型のコンストラクタの1つを選択し、その
throws
句を決定するために使用されます。Cの直接スーパークラス型に、適用可能かつアクセス可能な一意の最も的確なコンストラクタがない場合、コンパイル時にエラーが発生します(メソッド呼出しと同様)。
それ以外の場合、Cの無名コンストラクタはCのコンストラクタとして選択されます(15.9.5.1)。その本体は、Cの直接スーパークラス型で選択されたコンストラクタの明示的なコンストラクタ呼出し(8.8.7.1)で構成されます。
選択されたコンストラクタの
throws
句には、Cの直接スーパークラス型で選択されたコンストラクタのthrows
句の例外が含まれます。選択されたコンストラクタに対応する戻り型は匿名クラス型です。
クラス・インスタンス作成式で
<>
を使用する場合は、次のようになります。Cが匿名クラスではない場合は、DはCと同じであるとします。Cが匿名クラスである場合、Dはクラス・インスタンス作成式によって名前が付けられるCのスーパークラスまたはスーパーインタフェースであるとします。
この場合は、スーパークラスまたはスーパーインタフェース型ではなく、スーパークラスまたはスーパーインタフェースについて説明します。
Dがクラスである場合、c1...cnはクラスDのコンストラクタであるとします。Dがインタフェースである場合、c1...cnは、クラス
Object
の引数なしのコンストラクタを含むシングルトン・リスト(n = 1)であるとします。メソッドm1...mnのリストは、オーバーロード解決と型引数の推測の目的で定義されます。すべてのj (1 ≤ j ≤ n)について、mjは次のようにcjについて定義されます。
cjの型をインスタンス化するためにθjの置換が最初に定義されます。
F1...FpはDの型パラメータで、G1...Gqはcjの型パラメータ(ある場合)であるとします。X1...XpおよびY1...Yqは、Dの本体のスコープ内にない個別の名前を持つ型変数であるとします。
θjは
[F1:=X1, ..., Fp:=Xp, G1:=Y1, ..., Gq:=Yq]
です。mjの型パラメータは、X1...Xp,Y1...Yqです。各型パラメータの境界(ある場合)は、Dまたはcjの対応する型パラメータの境界に提供されるθjです。
mjの戻り型は、D
<
F1,...,Fp>
に適用されるθjです。mjの引数型の(場合によっては空の)リストは、cjの引数型に適用されるθjです。
mjのスローされる型の(場合によっては空の)リストは、cjのスローされる型に適用されるθjです。
mjの修飾子は、cjの修飾子です。
mjの名前は
#m
で、この名前はDのすべてのコンストラクタおよびメソッド名とは異なる、m1...mnによって共有される自動的に生成される名前です。mjの本体は無関係です。
コンストラクタを選択するために、一時的にm1...mnをDのメンバーとみなします。m1...mnの1つが選択されます(15.12.2に規定されているプロセスを使用して、クラス・インスタンス作成式の引数式によって決定します)。
適用可能かつアクセス可能な一意の最も的確なメソッドがない場合、コンパイル時にエラーが発生します。
それ以外の場合、mjが選択されたメソッドになる場所では、次のようになります。
Cが匿名クラスではない場合は、cjがCのコンストラクタとして選択されます。
選択されたコンストラクタの
throws
句は、mjに対して決定されるthrows
句と同じです。選択されたコンストラクタに対応する戻り型は、mjに対して決定される戻り型です(15.12.2.6)。
Cが匿名クラスである場合は、Cの無名コンストラクタがCのコンストラクタとして選択されます。その本体は、cjの明示的なコンストラクタ呼出し(8.8.7.1)で構成されます。
選択されたコンストラクタの
throws
句には、mjに対して決定されるthrows
句の例外が含まれます。選択されたコンストラクタに対応する戻り型は匿名クラス型です。
クラス・インスタンス作成式が多相的な式である場合、そのターゲット型との互換性は、mjを選択されたメソッドmとして使用して、18.5.2.1の規定のとおりになります。
ターゲット型との互換性のテストは、クラス・インスタンス作成式のターゲット型と、選択したコンストラクタに対応する戻り型の最終的な判定の前に、複数回発生する場合があります。たとえば、包含メソッド呼出し式では、異なるメソッドの仮パラメータ型との互換性についての、クラス・インスタンス作成式のテストが必要な場合があります。
Cが匿名クラスである場合は、その直接スーパークラス型または直接スーパーインタフェース型(15.9.5)は、mjに対して決定される戻り型です(15.12.2.6)。
直接スーパークラス型または直接スーパーインタフェース型あるいはそこの副次式(「副次式」ではパラメータ化された型の型引数、ワイルドカード型引数の境界および配列型の要素型が含まれますが、型変数の境界は除外されます)の形式が次のいずれかである場合、コンパイル時にエラーが発生します。
型パラメータとして宣言されていない型変数(キャプチャ変換によって生成される型変数など)。
交差型。
クラスまたはインタフェース宣言が、クラス・インスタンス作成式が出現するクラスまたはインタフェースからアクセス不可能である、クラスまたはインタフェース型。
クラス・インスタンス作成式の引数が、呼出しタイプ(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.1、8.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つを区別すると便利です。
SAが内部クラスではない場合、またはSAが静的コンテキストに出現するローカル・クラスである場合、無名コンストラクタには、Cが宣言されるクラス・インスタンス作成式またはenum定数の実際の引数ごとに1つの仮パラメータがあります。クラス・インスタンス作成式またはenum定数の実際の引数は、15.9.3に規定されているように、
SAのコンストラクタcscaの決定に使用されます。無名コンストラクタの各仮パラメータの型は、cscaの対応する仮パラメータと同じである必要があります。コンストラクタ本体は、
super(...)
の形式の明示的なコンストラクタ呼出し(8.8.7.1)で構成されます。この場合、実際の引数はコンストラクタの仮パラメータです(宣言された順序となります)。呼び出されるスーパークラス・コンストラクタはcscaです。それ以外の場合、Cのコンストラクタの最初の仮パラメータは、
SAに関してiを直接包含するインスタンスの値を表します(15.9.2、15.9.3)。このパラメータの型は、SAの宣言を直接包含するクラス型です。コンストラクタは、匿名クラスを宣言したクラス・インスタンス作成式の実際の引数ごとに、追加の仮パラメータを持ちます。n番目の仮パラメータeは、n-1番目の実際の引数に対応します。
クラス・インスタンス作成式の実際の引数は、15.9.3に規定されているように、
SAのコンストラクタcscaの決定に使用されます。無名コンストラクタの各仮パラメータの型は、cscaの対応する仮パラメータと同じである必要があります。コンストラクタ本体は、
o.super(...)
の形式の明示的なコンストラクタ呼出し(8.8.7.1)で構成されます。この場合、o
はコンストラクタの最初の仮パラメータであり、実際の引数はコンストラクタの後続の仮パラメータです(宣言された順序となります)。呼び出されるスーパークラス・コンストラクタはcscaです。
すべての場合に、無名コンストラクタのthrows
句は、15.9.3に規定されているように、無名コンストラクタ内に含まれる明示的なスーパークラス・コンストラクタ呼出し文によってスローされるすべてのチェック例外と、匿名クラスのインスタンス・イニシャライザまたはインスタンス変数イニシャライザによってスローされるすべてのチェック例外をリストする必要がありますリストします。
無名コンストラクタのシグネチャは、アクセス不可能な型を参照できます(たとえば、そのような型がスーパークラス・コンストラクタcsのシグネチャに出現した場合など)。これ自体では、コンパイル時にも実行時にもエラーが発生することはありません。
15.12 メソッド呼出し式
15.12.1 コンパイル時のステップ1: 検索するクラスまたはインタフェース型の特定
コンパイル時にメソッド呼出しを処理するときの最初のステップは、呼び出されるメソッドの名前と、その名前のメソッドの定義を検索するクラスまたはインタフェース型を特定することです。
メソッドの名前は、MethodInvocationの左カッコの直前にあるMethodNameまたはIdentifierによって指定されます。
検索するクラスまたはインタフェース型については、MethodInvocationの左カッコの前にある形式に応じて、次の6つのケースを検討します。
形式がMethodName、つまり単にIdentifierである場合は、次のようになります。
Identifierが、その名前を持つメソッド宣言のスコープ内に出現する場合(6.3、6.4.1)、次のようになります。
そのメソッドがメンバーである包含
型クラスまたはインタフェース宣言がある場合、TEは、最も内側のそのような型クラスまたはインタフェース宣言であるとします。検索するクラスまたはインタフェース型は、TE.this
(15.8.4)の型です。この検索ポリシーは、コーム・ルールと呼ばれます。実質的には、ネストしたクラスのスーパークラス階層内でメソッドを検索してから、包含クラスおよびそのスーパークラス階層内でメソッドを検索します。例については、6.5.7.1を参照してください。
それ以外の場合、1つ以上の単一静的インポートまたはオンデマンド静的インポート宣言により、メソッド宣言はスコープ内にある可能性があります。呼び出されるメソッドは後で特定されるため(15.12.2.1)、検索する
クラスまたはインタフェース型はありません。
形式がTypeName
.
[TypeArguments] Identifierである場合、検索する型は、TypeNameが示す(場合によってはRAW)型です。形式がExpressionName
.
[TypeArguments] Identifierである場合、検索するクラスまたはインタフェース型は、ExpressionNameが示す変数の宣言された型Tであるか(Tがクラスまたはインタフェース型である場合)、Tの上限です(Tが型変数である場合)。形式がPrimary
.
[TypeArguments] Identifierである場合、Tは、Primary式の型であるとします。検索するクラスまたはインタフェース型は、Tであるか(Tがクラスまたはインタフェース型である場合)、Tの上限です(Tが型変数である場合)。Tが参照型でない場合は、コンパイル時にエラーが発生します。
形式が
super
.
[TypeArguments] Identifierである場合、検索するクラス型は、宣言にメソッド呼出しが含まれているクラスの直接スーパークラス型です。TEは、メソッド呼出しを直接包含する型宣言クラスまたはインタフェース宣言であるとします。TEがクラスObject
であるか、TEがインタフェースである場合は、コンパイル時にエラーが発生します。形式がTypeName
.
super
.
[TypeArguments] Identifierである場合は、次のようになります。TypeNameがクラスもインタフェースも示さない場合、コンパイル時にエラーが発生します。
TypeNameがクラスCを示す場合、検索する
クラス型は、Cの直接スーパークラス型です。Cが現在のクラスまたはインタフェースの字句的な包含
型クラスまたはインタフェース宣言でない場合、あるいはCがクラスObject
である場合は、コンパイル時にエラーが発生します。TEは、メソッド呼出しを直接包含する型クラスまたはインタフェース宣言であるとします。TEがクラスObject
である場合は、コンパイル時にエラーが発生します。それ以外の場合、TypeNameは、
検索するインタフェースインタフェースIを示します。TEは、メソッド呼出しを直接包含する型クラスまたはインタフェース宣言であるとします。
IがTEの直接スーパーインタフェースでない場合、またはTEに他の直接スーパークラスまたは直接スーパーインタフェースJが存在し、JがIのサブタイプサブクラスまたはサブインタフェースであるような場合は、コンパイル時にエラーが発生します。検索する型は、Eの直接スーパーインタフェース型であるIの型です。
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)である必要があります。そうでない場合はコンパイル時にエラーが発生します。このようなメソッド呼出しは値を生成しないため、値が不要な状況でのみ使用する必要があります。
さらに、コンパイル時宣言が適切であるかどうかは、次のように、左カッコの前にあるメソッド呼出し式の形式によって異なることがあります。
形式がMethodName、つまり単にIdentifierであり、かつコンパイル時宣言がインスタンス・メソッドである場合は、次のようになります。
メソッド呼出しが静的コンテキストに出現する場合(8.1.3)、コンパイル時にエラーが発生します。
それ以外の場合、Cは、コンパイル時宣言がメンバーである、直接包含するクラスまたはインタフェース宣言であるとします。メソッド呼出しがCまたはCの内部クラスによって直接包含されていない場合は、コンパイル時にエラーが発生します。
形式がTypeName
.
[TypeArguments] Identifierである場合、コンパイル時宣言はstatic
である必要があります。そうでない場合はコンパイル時にエラーが発生します。形式がExpressionName
.
[TypeArguments] IdentifierまたはPrimary.
[TypeArguments] Identifierである場合、コンパイル時宣言は、インタフェースで宣言されたstatic
メソッドでないことが必要です。そうである場合はコンパイル時にエラーが発生します。形式が
super
.
[TypeArguments] Identifierである場合は、次のようになります。コンパイル時宣言が
abstract
である場合、コンパイル時にエラーが発生します。メソッド呼出しが静的コンテキストに出現する場合、コンパイル時にエラーが発生します。
形式がTypeName
.
super
.
[TypeArguments] Identifierである場合は、次のようになります。コンパイル時宣言が
abstract
である場合、コンパイル時にエラーが発生します。メソッド呼出しが静的コンテキストに出現する場合、コンパイル時にエラーが発生します。
TypeNameがクラスCを示す場合、メソッド呼出しがCまたはCの内部クラスによって直接包含されていない場合は、コンパイル時にエラーが発生します。
TypeNameがインタフェースを示す場合、
TEは、メソッド呼出しを直接包含する型クラスまたはインタフェース宣言であるとします。コンパイル時宣言をTEの直接スーパークラスまたは直接スーパーインタフェースからオーバーライドする(9.4.1)、コンパイル時宣言とは異なるメソッドが存在する場合、コンパイル時にエラーが発生します。親インタフェースの親で宣言されたメソッドをスーパーインタフェースがオーバーライドする場合、このルールでは、親の親を直接スーパーインタフェースのそのリストに単に追加することによって、子インタフェースがオーバーライドをスキップすることが回避されます。親の親の機能にアクセスするための適切な方法は、直接スーパーインタフェースを介して、そのインタフェースが目的の動作を公開することを選択する場合にのみ、アクセスすることです。(また、プログラマは、
super
メソッド呼出しで目的の動作を公開する追加のスーパーインタフェースを自由に定義できます。)
...
15.12.4.3 型およびメソッドのアクセス可能性のチェック
このセクションでは:
Dは、メソッド呼出しを含むクラスであるとします。
TQは、メソッド呼出しの修飾型クラスまたはインタフェースであるとします(13.1)。mは、コンパイル時に決定するメソッドの名前であるとします(15.12.3)。
Javaプログラミング言語の実装により、リンケージの一部として、型TクラスまたはインタフェースQがアクセス可能になる必要があります。
TQがDと同じパッケージ内にある場合、TQはアクセス可能です。TQがDとは異なるパッケージ内にあり、そのパッケージが同じモジュール内にあり、TQがpublic
またはprotected
である場合、TQはアクセス可能です。TQがDとは異なるパッケージ内にあり、異なるモジュール内にそのパッケージがあり、TQのモジュールがTQのパッケージをDのモジュールにエクスポートし、TQがpublic
またはprotected
である場合、TQはアクセス可能です。
TQがprotected
である場合、これは必然的にネストされた型クラスまたはインタフェースになるため、コンパイル時に、そのアクセス可能性は、その宣言を包含する型クラスまたはインタフェースのアクセス可能性の影響を受けます。ただし、リンケージ中は、そのアクセス可能性は、その宣言を包含する型クラスまたはインタフェースのアクセス可能性の影響を受けません。さらにリンケージ中は、protected
のTQは、public
のTQとしてアクセス可能です。このようなコンパイル時のアクセス制御(6.6)と実行時のアクセス制御の相違の原因は、Java仮想マシンの制限です。
実装により、リンケージ中に、メソッドmが引き続きTQあるいはTQのスーパータイプスーパークラスまたはスーパーインタフェースに見つかるようになる必要もあります。mが見つからない場合、NoSuchMethodError
(IncompatibleClassChangeError
のサブクラス)が発生します。mが見つかる場合、Cは、mを宣言する型クラスまたはインタフェースであるとします。実装により、リンケージ中に、Cのmの宣言がDにアクセスできるようになる必要があります。
mが
public
の場合、mはアクセス可能です。mが
protected
であるときに、(i) DがCと同じパッケージ内にあるか、DがCのサブタイプサブクラスまたはC自体である場合に限り、mはアクセス可能です。(ii) mがprotected
インスタンス・メソッドである場合、TQはDのサブタイプサブクラスまたはD自体である必要があります。protected
インスタンス・メソッドは、インボーカの型に合致する修飾型クラスまたはインタフェースを介してのみ呼び出すことができるため、これがmのチェックにTQが関与する唯一の場所となります。mにパッケージ・アクセス権がある場合、DがCと同じパッケージ内にある場合に限り、mはアクセス可能です。
mが
private
であるときに、DがCであるか、DがCを囲むか、CがDを囲むか、CとDの両方が3つ目の型クラスまたはインタフェースで囲まれる場合に限り、mはアクセス可能です。
TQとmのいずれかがアクセス不可能である場合、IllegalAccessError
が発生します(12.3)。
呼出しモードがinterface
の場合、実装によって、ターゲット参照型クラスが、指定されたインタフェースを引き続き実装することをチェックする必要があります。ターゲット参照型クラスがインタフェースを引き続き実装しない場合、IncompatibleClassChangeError
が発生します。
15.12.4.4 呼び出すメソッドの特定
前のセクション(15.12.4.3)と同様に、次のようになります。
TQは、メソッド呼出しの修飾型クラスまたはインタフェースであるとします(13.1)。メソッドmは、
TQあるいはTQのスーパータイプスーパークラスまたはスーパーインタフェースに見つかるメソッドであるとします。(mは、前のセクションでは単なるメソッドの名前です。ここでは、実際の宣言です。)Cは、mを宣言するクラスまたはインタフェースであるとします。
呼び出すメソッドを特定するための戦略は、呼出しモードに応じて異なります。
呼出しモードが
static
の場合、ターゲット参照は必要なく、オーバーライドは許可されません。クラスまたはインタフェースCのメソッドmが呼び出されます。それ以外の場合、インスタンス・メソッドが呼び出され、ターゲット参照が存在します。ターゲット参照が
null
の場合は、このポイントでNullPointerException
がスローされます。そうでない場合、ターゲット参照はターゲット・オブジェクトを参照すると言い表され、呼び出されるメソッドのキーワードthis
の値として使用されます。その後、呼出しモードの他の3つの可能性が検討されます。呼出しモードが
super
の場合、オーバーライドは許可されません。クラスまたはインタフェースCのメソッドmが呼び出されます。mがabstract
の場合、AbstractMethodError
がスローされます。そうでない場合に、呼出しモードが
virtual
で、Tとmが一緒にシグネチャ多相メソッド(15.12.3)を示す場合、ターゲット・オブジェクトはjava.lang.invoke.MethodHandle
またはjava.lang.invoke.VarHandle
のインスタンスです。ターゲット・オブジェクトは、コンパイル時のメソッド呼出しに関連付けられた情報に対して一致する状態をカプセル化します。この一致の詳細は、Java仮想マシン仕様、Java SE 15 EditionとJava SEプラットフォームAPIに示されています。一致が成功した場合、java.lang.invoke.MethodHandle
インスタンスによって参照されるメソッドがすぐに直接呼び出されるか、java.lang.invoke.VarHandle
インスタンスによって表される変数にすぐに直接アクセスされます。いずれの場合にも、15.12.4.5の手順は実行されません。一致が失敗した場合、java.lang.invoke.WrongMethodTypeException
がスローされます。それ以外の場合、呼出しモードは
interface
またはvirtual
です。クラスまたはインタフェースCのメソッドmが
private
である場合、これが呼び出されるメソッドです。そうでない場合、オーバーライドが発生する可能性があります。次に示すダイナミック・メソッド参照は、呼び出すメソッドの特定に使用されます。参照手順は、ターゲット・オブジェクトの実際の実行時クラスであるクラスRから開始します。
呼出しモード
interface
の場合、Rは必然的にTQを実装します。呼出しモードvirtual
の場合、Rは必然的にTQまたはTQのサブクラスになります。ターゲット・オブジェクトが配列の場合、Rは、配列型を表す「クラス」です。
...
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の場合、次のようになります。
TypeNameがクラスCを示す場合に、Cが現在のクラスの字句的な包含クラスではない場合、またはCがクラス
Object
である場合は、コンパイル時にエラーが発生します。TypeNameがインタフェースIを示す場合、
TEは、メソッド参照式を直接包含する型クラスまたはインタフェース宣言であるとします。IがTEの直接スーパーインタフェース型によって名前が付けられていない場合、またはJとしてクラスまたはインタフェースに名前を付ける他の直接スーパークラス型または直接スーパーインタフェース型がTEに存在し、JがIのサブタイプサブクラスまたはサブインタフェースであるような場合は、コンパイル時にエラーが発生します。TypeNameが型変数を示す場合、コンパイル時にエラーが発生します。
メソッド参照式の形式がsuper
::
[TypeArguments] IdentifierまたはTypeName .
super
::
[TypeArguments] Identifierの場合、式が静的コンテキストに出現するとコンパイル時にエラーが発生します。
メソッド参照式の形式がClassType ::
[TypeArguments] new
の場合、次のようになります。
ClassTypeは、アクセス可能(6.6)であり、非
abstract
であり、enum型クラスではないクラスを表すに名前を付ける必要があります。そうでない場合、コンパイル時にエラーが発生します。ClassTypeが、パラメータ化された型(4.5)を示す場合に、そのいずれかの型引数がワイルドカードであると、コンパイル時にエラーが発生します。
ClassTypeが、RAW型(4.8)を示す場合に、
::
の後にTypeArgumentsが存在すると、コンパイル時にエラーが発生します。
メソッド参照式の形式がArrayType ::
new
の場合、ArrayTypeは、具象化可能(4.7)である型を示している必要があります。そうでない場合、コンパイル時にエラーが発生します。
...