第9章 インタフェース

目次

9.1 インタフェース宣言
9.1 インタフェース修飾子
9.1.1.1. abstractインタフェース
9.1.1.2. strictfpインタフェース
9.1.1.3. staticインタフェース
9.1.1.4. sealedおよびnon-sealedインタフェース
9.1 汎用インタフェースおよびタイプ・パラメータ
9.1 スーパーインタフェースとサブインタフェース
9.1 許可される直接サブクラスおよびサブインタフェース
9.1 インタフェース本体およびメンバー宣言
9.2 インタフェース・メンバー
9.3 フィールド(定数)デクラレーション
9.3 インタフェースでのフィールドの初期化
9.4 メソッド宣言
9.4 継承およびオーバーライド
9.4.1.1. (インスタンス・メソッドによる)オーバーライド
9.4.1.2. 上書き時の要件
9.4.1.3. Override-Equivalentシグネチャを使用したメソッドの継承
9.4 オーバーロード
9.4 インタフェース・メソッド本体
9.5 メンバー・クラスおよびインタフェース宣言
9.6 注釈インタフェース
9.6 注釈インタフェース要素
9.6 注釈インタフェース要素のデフォルト
9.6 繰返し可能な注釈インタフェース
9.6 事前定義済の注釈インタフェース
9.6.4.1. @Target
9.6.4.2. @Retention
9.6.4.3. @Inherited
9.6.4.4. @Override
9.6.4.5. @SuppressWarnings
9.6.4.6. @Deprecated
9.6.4.7. @SafeVarargs
9.6.4.8. @Repeatable
9.6.4.9. @FunctionalInterface
9.7 注釈
9.7 通常の注釈
9.7 マーカー注釈
9.7 単一要素注釈
9.7 注釈が表示される場所
9.7 同じインタフェースの複数の注釈
9.8 機能インタフェース
9.9 関数タイプ

インタフェース宣言は、1つ以上のクラスによって実装できる新しいインタフェースを定義します。 プログラムは、インタフェースを使用して、その他の関係のないクラスに共通のスーパータイプを提供し、関連するクラスが共通のabstractスーパークラスを共有する必要がないようにすることができます。

インタフェースにはインスタンス変数がなく、通常は1つ以上のabstractメソッドを宣言します。それ以外の場合、関連のないクラスは、そのabstractメソッドの実装を提供することでインタフェースを実装できます。 インタフェースは直接インスタンス化できません。

最上位インタフェース (§7.6)は、コンパイル単位で直接宣言されたインタフェースです。

ネストされたインタフェースは、別のクラスまたはインタフェース宣言の本体内で宣言が発生するインタフェースです。 ネストされたインタフェースは、メンバー・インタフェース(§8.5§9.5)またはローカル・インタフェース(§14.3)です。

注釈インタフェース(§9.6)は、個別の構文で宣言されたインタフェースであり、注釈(§9.7)の反射表現によって実装されることを意図しています。

この章では、すべてのインタフェースの共通セマンティクスについて説明します。 特定の種類のインタフェースに固有の詳細については、これらの構造体専用のセクションで説明します。

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

クラスは、1つ以上のインタフェース(§8.1.5)を直接実装するように宣言できます。つまり、クラスのどのインスタンスも、インタフェースで指定されたすべてのabstractメソッドを実装します。 クラスは、その直接スーパークラスおよび直接スーパー・インタフェースが実行するすべてのインタフェースを実装する必要があります。 この(multiple)インタフェース継承により、オブジェクトはスーパークラスを共有せずに(multiple)共通の動作をサポートできます。

クラスとは異なり、インタフェースはfinalとして宣言できません。 ただし、インタフェースをsealed (§9.1.1.4)と宣言して、そのサブクラスおよびサブインタフェースを制限できます。

宣言された型がインタフェース型である変数は、その値として、指定されたインタフェースを実装するクラスの任意のインスタンスへの参照を持つことができます。 クラスがインタフェースのすべてのabstractメソッドを実装するだけでは不十分です。クラスまたはそのスーパークラスの1つを実際に宣言してインタフェースを実装するか、クラスがインタフェースを実装するとみなされません。

9.1.  インタフェース宣言

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

インタフェース宣言には、通常のインタフェース宣言注釈インタフェース宣言(§9.6)の2種類があります。

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

インタフェースの単純名が、それを囲むクラスまたはインタフェースのいずれかと同じ場合、コンパイル時エラーになります。

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

9.1.1.  インタフェース修飾子

インタフェース宣言には、インタフェース修飾子を含めることができます。

InterfaceModifier:
(いずれか)
注釈 public protected private
abstract static sealed non-sealed strictfp

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

アクセス修飾子public (§6.6)は、ローカル・インタフェース(§14.3)ではなく、トップ・レベル・インタフェース(§7.6)およびメンバー・インタフェース(§8.5§9.5)にのみ関連します。

アクセス修飾子protectedおよびprivateは、メンバー・インタフェースにのみ関係します。

修飾子staticは、メンバー・インタフェースおよびローカル・インタフェースにのみ関係します。

同じキーワードがインタフェース宣言の修飾子として複数回出現する場合、またはインタフェース宣言にアクセス修飾子publicprotectedおよびprivateが複数存在する場合は、コンパイル時にエラーが発生します。

インタフェース宣言に複数の修飾子sealedおよびnon-sealedがある場合、コンパイル時にエラーが発生します。

2つ以上の(個別)インタフェース修飾子がインタフェース宣言に表示される場合、InterfaceModifierの本番で前述したものと一致する順序で表示されることは、必須ではありませんが慣例です。

9.1.1.1. abstractインタフェース

すべてのインタフェースは暗黙的にabstractです。

この修飾子は廃止されているため、新しいコードでは使用しないでください。

9.1.1.2. strictfpインタフェース

インタフェース宣言のstrictfp修飾子は廃止されており、新しいコードでは使用しないでください。 その存在または不在は、コンパイル時または実行時に影響しません。

9.1.1.3. staticインタフェース

ネストされたインタフェースは、暗黙的にstaticです。 つまり、すべてのメンバー・インタフェースおよびローカル・インタフェースはstaticです。 メンバー・インタフェースの宣言でstatic修飾子(§9.5)を重複して指定することは許可されていますが、ローカル・インタフェースの宣言には許可されていません(§14.3)。

ネストされたインタフェースはstaticであるため、すぐに包含されるインスタンスはありません(§8.1.3)。 ネストされたインタフェースから型パラメータ、インスタンス変数、ローカル変数、仮パラメータ、例外パラメータ、または字句的に包含するクラス、インタフェースまたはメソッド宣言のインスタンス・メソッドへの参照は許可されません(§6.5.5.1§6.5.6.1§15.12.3)。

9.1.1.4. sealedおよびnon-sealedインタフェース

インタフェースの宣言時にすべての直接サブクラスおよび直接サブインタフェースが認識され(§9.1.4)、その他の直接サブクラスまたは直接サブインタフェースが望まれない場合、インタフェースはsealedとして宣言できます。

クラスは、その直接スーパーインタフェースの直接サブクラスである(§8.1.5)ことを思い出しておくと役に立ちます。

インタフェースの直接スーパーインタフェースがsealed (§9.1.3)でなく、sealed自体ではない場合、インタフェースは完全に拡張可能です。

sealed直接スーパーインタフェースを持つインタフェースは、non-sealedと宣言されている場合にのみ、自由に拡張できます。

インタフェースにsealed直接スーパーインタフェースがあり、sealedまたはnon-sealedが宣言されていない場合、コンパイル時にエラーが発生します。

インタフェースがnon-sealedと宣言されているが、sealed直接スーパーインタフェースがない場合、コンパイル時にエラーが発生します。

9.1.2.  汎用インタフェースおよび型パラメータ

インタフェース宣言で1つ以上の型変数が宣言されている場合、インタフェースは汎用です(§4.4)。

これらの型変数は、インタフェースの型パラメータと呼ばれます。 タイプ・パラメータ・セクションは、インタフェース名の後に山カッコで区切られます。

ここでは、便宜上、§8.1.2および §4.4からの次のプロダクションを示します。

TypeParameters:
TypeParameterList:
TypeParameterModifier:
TypeBound:
AdditionalBound:

型パラメータ宣言の注釈修飾子に関する規則は、§9.7.4および§9.7.5に記載されています。

インタフェースの型パラメータ・セクションで、型変数T型変数Sに直接依存します(STの境界である場合)。一方、TSに依存します(TSまたはTに直接依存している場合)。この定義は、Sに依存する型変数Uに直接依存します(この定義を再帰的に使用します)。 インタフェース型パラメータ・セクションの型変数がそれ自体に依存している場合は、コンパイル時エラーです。

インタフェースの型パラメータのスコープとシャドウは、§6.3および §6.4.1で指定されています。

静的コンテキストまたはネストされたクラスまたはインタフェースからのインタフェースの型パラメータへの参照は、§6.5.5.1で指定されているように制限されます。

汎用インタフェース宣言は、パラメータ化された型のセット(§4.5)を定義し、型引数による型パラメータ・セクションの可能なパラメータ化ごとに1つずつ定義します。 これらのパラメータ化された型はすべて、実行時に同じインタフェースを共有します。

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

extends句が指定されている場合、宣言されるインタフェースは、指定された各インタフェース・タイプを拡張するため、これらの各インタフェース・タイプのメンバー・クラス、メンバー・インタフェース、インスタンス・メソッドおよびstaticフィールドを継承します。

指定されたインタフェース・タイプは、宣言されるインタフェースのダイレクト・スーパーインタフェース・タイプです。

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

InterfaceExtends:

ここでは、便宜上、§8.1.5の次の本番環境を示します。

InterfaceTypeList:

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

いずれかのInterfaceTypesealed (§9.1.1.4)のインタフェースに名前を付け、宣言されているインタフェースが指定されたインタフェース(§9.1.4)の直接サブインタフェースとして許可されていない場合、コンパイル時にエラーが発生します。

InterfaceTypeに型引数がある場合、整形式のパラメータ化された型(§4.5)を示す必要があり、どの型引数もワイルドカード型引数にできないか、コンパイル時にエラーが発生します。

1つ目のインタフェースは、2つ目のインタフェースの直接スーパーインタフェース・タイプの1つによって名前が付けられている場合、別のインタフェースの直接スーパーインタフェースです。

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

  • IKの直接スーパーインタフェースである。

  • ここで、JKの直接スーパーインタフェースであり、IJのスーパーインタフェースであり、この定義を再帰的に適用します。

インタフェースは、その直接スーパーインタフェースの直接サブインタフェースであり、各スーパーインタフェースのサブインタフェースであると言われています。

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

インタフェースIは、クラスまたはインタフェースAAIextends句で、スーパーインタフェースとして、またはスーパーインタフェース名の完全修飾形式の修飾子として記述されている場合、直接依存します

次のいずれかに該当する場合、インタフェースIはクラスまたはインタフェースAに依存します。

  • IAに直接依存する。

  • Iは、A (§8.1.5)に依存するクラスCに直接依存します。

  • Iが、Aに依存するインタフェースJに直接依存する(この定義を再帰的に適用)。

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

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

9.1.4.  許容された直接サブクラスおよびサブインタフェース

標準インタフェース宣言のオプションのpermits句は、宣言されるインタフェースの直接サブクラスおよび直接サブインタフェース(§9.1.1.4)として意図されるすべてのクラスおよびインタフェースを指定します。

InterfacePermits:
permits TypeName {, TypeName}

インタフェース宣言にpermits句があり、sealed修飾子がない場合、コンパイル時にエラーが発生します。

すべてのTypeNameは、アクセス可能なクラスまたはインタフェースに名前を付ける必要があります(§6.6)。そうしないと、コンパイル時にエラーが発生します。

permits句で同じクラスまたはインタフェースが複数回指定されている場合、コンパイル時にエラーが発生します。 これは、クラスまたはインタフェースに異なる方法で名前が付けられている場合でも当てはまります。

クラスまたはインタフェースの正規名は、permits句で使用する必要はありませんが、permits句で指定できるのはクラスまたはインタフェースを1回のみです。 たとえば、次のプログラムはコンパイルできません。

package p;

sealed interface I permits C, D, p.C {}  // error

non-sealed class C implements I {}
non-sealed class D implements I {}

sealedインタフェースIが名前付きモジュール(§7.3)に関連付けられている場合は、Iの宣言のpermits句で指定されたすべてのクラスまたはインタフェースをIと同じモジュールに関連付ける必要があります。そうしないと、コンパイル時にエラーが発生します。

sealedインタフェースIが名前のないモジュール(§7.7.5)に関連付けられている場合、Iの宣言のpermits句で指定されているすべてのクラスまたはインタフェースは、Iと同じパッケージに属している必要があります。そうしないと、コンパイル時にエラーが発生します。

sealedインタフェースとそのダイレクト・サブクラスおよびダイレクト・サブインタフェースは、それぞれpermitsimplementsおよびextends句で、循環方式で相互を参照する必要があります。 したがって、モジュラ・コードベースでは、異なるモジュールのクラスとインタフェースが循環方式で相互に参照できないため、同じモジュール内に配置する必要があります。 シール済インタフェース階層は常に1つのメンテナンス・ドメイン内で宣言する必要があり、同じ開発者または開発者のグループが階層の維持を担当するため、いずれの場合もコロケーションが望ましいです。 名前付きモジュールは通常、モジュラーコードベースの保守ドメインを表します。

sealedインタフェースIの宣言にpermits句がある場合、I許可された直接サブクラスおよびサブインタフェースは、permits句で指定されたクラスおよびインタフェースです。

permits句で指定されるすべての許可される直接サブクラスおよびサブインタフェースは、I (§8.1.5)の直接サブクラスまたはI (§9.1.3)の直接サブインタフェースである必要があります。そうでない場合、コンパイル時にエラーが発生します。

sealedインタフェースIの宣言にpermits句がない場合、Iで許可される直接サブクラスおよびサブインタフェースは、正規名(§6.7)を持ち、直接スーパーインタフェースにIが含まれるI (§7.3)と同じコンパイル・ユニットで宣言されるクラスおよびインタフェースです。

つまり、許可される直接サブクラスおよびサブインタフェースは、直接スーパーインタフェースとして Iを指定する同じコンパイルユニット内のクラスおよびインタフェースと見なされます。 正規名の要件は、ローカル・クラス、ローカル・インタフェースまたは匿名クラスが考慮されないことを意味します。

sealedインタフェースIの宣言にpermits句がなく、Iに許可されている直接サブクラスまたはサブインタフェースがない場合、コンパイル時にエラーが発生します。

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

インタフェース本体には、インタフェースのメンバー、つまりフィールド(§9.3)、メソッド(§9.4)、クラスおよびインタフェース(§9.5)の宣言を含めることができます。

インタフェースIで宣言または継承されたメンバーmの宣言のスコープは、§6.3で指定されます。

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

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

  • インタフェース宣言の本体で宣言されたメンバー(§9.1.5)。

  • 任意の直接スーパーインタフェース型から継承されたメンバー(§9.1.3)。

  • インタフェースに直接スーパーインタフェース型がない場合、インタフェースは、シグネチャs、戻り型rおよびthrowstを持つpublic abstractメンバー・メソッドmを暗黙的に宣言し、各publicインスタンス・メソッドmに対応するthrowstを使用します。シグネチャs、戻り型rおよびthrowstObjectで宣言されている場合(§4.3.2)、同じシグネチャ、同じ戻り型および互換性のあるthrows句を持つabstractメソッドがインタフェースによって明示的に宣言されている場合を除きます。

    Objectmfinalとして宣言されている場合、インタフェースがそのようなメソッドmを明示的に宣言すると、コンパイル時にエラーが発生します。

    インタフェースがObjectpublicメソッドに対してオーバーライド等価(§8.4.2)のシグネチャを持つメソッドを明示的に宣言し、戻り型が異なるか、互換性のないthrows句があるか、またはabstractでない場合、コンパイル時にエラーが発生します。

このインタフェースは、(i)非表示にするフィールド、クラスおよびインタフェース、(ii) abstractメソッドおよびオーバーライドするデフォルト・メソッド(§9.4.1)、(iii) privateメソッドおよび(iv) staticメソッドを除いて、拡張するインタフェースからこれらのインタフェースのすべてのメンバーを継承します。

インタフェースのフィールド、メソッド、メンバー・クラスおよびメンバー・インタフェースは、異なるコンテキストで使用され、異なる参照プロシージャ(§6.5)によって曖昧化されるため、同じ名前を持つ場合があります。 ただし、スタイルの面からお薦めできません。

9.3.  フィールド(定数)宣言

ConstantModifier:
(いずれか)
Annotation public
static final

UnannTypeについては、§8.3を参照してください。 ここでは、便宜上、§4.3および §8.3からの次のプロダクションを示します。

VariableDeclaratorList:
VariableDeclarator:
VariableDeclaratorId:
ディメンション:
{注釈} [ ] {{注釈} [ ]}
VariableInitializer:

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

インタフェース宣言の本体のすべてのフィールド宣言は、暗黙的にpublicstaticおよびfinalです。 このようなフィールドには、これらの修飾子のいずれかまたはすべての重複指定が許可されています。

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

2つ以上の(個別)フィールド修飾子がフィールド宣言に表示される場合、ConstantModifierの本番で前述したものと一致する順序で表示されることは、必須ではありませんが慣例です。

宣言されたフィールドの型は、UnannTypeVariableDeclaratorIdにカッコのペアが表示されない場合、UnannTypeで示され、それ以外の場合は§10.2で指定されます。

インタフェース・フィールドのすべての宣言に識別子が含まれている必要があります。そうしないと、コンパイル時にエラーが発生します。

インタフェース・フィールド宣言のスコープおよびシャドウ化は、§6.3および§6.4.1で指定されています。

インタフェース・フィールドはstaticであるため、この宣言では静的コンテキスト(§8.1.3)が導入され、現在のオブジェクトを参照する構造体の使用が制限されます。 特に、キーワードthisおよびsuperは、静的コンテキスト(§15.8.3§15.11.2)では禁止されており、字句的に包含する宣言のインスタンス変数、インスタンス・メソッドおよび型パラメータへの修飾されていない参照です(§6.5.5.1§6.5.6.1§15.12.3)。

インタフェース宣言の本体で、同じ名前のフィールドを複数宣言すると、コンパイル時にエラーが発生します。

インタフェースが特定の名前を持つフィールドを宣言すると、そのフィールドの宣言は、インタフェースのスーパーインタフェースで同じ名前を持つフィールドのすべてのアクセス可能な宣言を非表示にします。

インタフェースは、同じ名前の複数のフィールドを継承できます。 このような状況自体では、コンパイル時エラーは発生しません。 ただし、インタフェース宣言の本体内でそのようなフィールドを単純名で参照しようとすると、参照があいまいなため、コンパイル時にエラーが発生します。

インタフェースから同じフィールドの宣言が継承されるパスが複数ある場合があります。 このような状況下では、フィールドは1回のみ継承されたとみなされ、あいまいさなしに単純名で参照できます。

例9.3-1.  あいまいな継承フィールド

たとえば、2つの直接スーパーインタフェースがその名前でフィールドを宣言するため、同じ名前の2つのフィールドが1つのインタフェースに継承されると、単一のあいまいなメンバー結果が生じます。 このあいまいなメンバーを使用すると、コンパイル時にエラーが発生します。 プログラム内では次のようになります。

interface BaseColors {
    int RED = 1, GREEN = 2, BLUE = 4;
}
interface RainbowColors extends BaseColors {
    int YELLOW = 3, ORANGE = 5, INDIGO = 6, VIOLET = 7;
}
interface PrintColors extends BaseColors {
    int YELLOW = 8, CYAN = 16, MAGENTA = 32;
}
interface LotsOfColors extends RainbowColors, PrintColors {
    int FUCHSIA = 17, VERMILION = 43, CHARTREUSE = RED+90;
}

インタフェースLotsOfColorsは、YELLOWという名前の2つのフィールドを継承します。 インタフェースにフィールドYELLOWへの単純な名前による参照が含まれていないかぎり、これは問題ありません。 (このような参照は、フィールドの変数イニシャライザ内で発生する可能性があります。)

インタフェースPrintColorsが値8ではなく値3YELLOWに指定する場合でも、インタフェースLotsOfColors内のフィールドYELLOWへの参照はあいまいであるとみなされます。


例9.3-2.  継承フィールドの乗算

たとえば、このインタフェースとこのインタフェースの直接スーパーインタフェースの両方がフィールドを宣言するインタフェースを拡張するなど、単一のフィールドが同じインタフェースから複数回継承される場合、単一のメンバー結果だけが生じます。 このような状況自体によってコンパイル時にエラーが発生することはありません。

前述の例では、フィールドREDGREENおよびBLUEは、インタフェースRainbowColorsおよびインタフェースPrintColorsを介して、インタフェースLotsOfColorsによって複数の方法で継承されますが、インタフェースLotsOfColorsのフィールドREDへの参照は、フィールドREDの実際の宣言が1つのみ含まれるため、あいまいとはみなされません。


9.3.1. インタフェースでのフィールドの初期化

インタフェースのフィールド宣言内のすべての宣言子には変数イニシャライザが必要です。そうしないと、コンパイル時にエラーが発生します。

イニシャライザは定数式である必要はありません(§15.29)。

インタフェース・フィールドのイニシャライザが同じインタフェースのイニシャライザ(§3.5)の右側に宣言が出現する同じフィールドの単純名または別のフィールドを使用する場合、コンパイル時にエラーが発生します。

インタフェース・フィールドのイニシャライザは、§15.8.3§15.11.2および§15.12.3で指定されているキーワードthisまたはキーワードsuperを使用して、現在のオブジェクトを参照することはできません。

実行時に、イニシャライザが評価され、インタフェースの初期化時にフィールド割当てが1回のみ実行されます(§12.4.2)。

定数変数であるインタフェース・フィールド(§4.12.4)は、他のインタフェース・フィールドより前に初期化されることに注意してください。 これは、クラス内の定数変数であるstaticフィールドにも適用されます(§8.3.2)。 このようなフィールドは、逸脱したプログラムであっても、デフォルトの初期値(§4.12.5)を持つことは決してありません。

例9.3.1-1 フィールドへの参照の転送

interface Test {
    float f = j;
    int   j = 1;
    int   k = k + 1;
}

このプログラムでは、jが宣言される前にfの初期化でjが参照され、kの初期化がk自体を参照するため、2つのコンパイル時エラーが発生します。


9.4.  メソッド宣言

InterfaceMethodDeclaration:
InterfaceMethodModifier:
(いずれか)
Annotation public private
abstract default static strictfp

ここでは、便宜上、§8.4§8.4.5、および §8.4.7からの次のプロダクションを示します。

インタフェース・メソッド宣言の注釈修飾子に関するルールは、§9.7.4および§9.7.5で指定されています。

インタフェース宣言の本体内のメソッドは、publicまたはprivate (§6.6)として宣言できます。 アクセス修飾子が指定されていない場合、メソッドは暗黙的にpublicになります。 スタイルの問題として、インタフェース宣言のメソッド宣言にpublic修飾子を重複して指定することは許可されますが、お薦めしません。

デフォルト・メソッドは、default修飾子を使用してインタフェースで宣言されたインスタンス・メソッドです。 その本文は常にブロックで表されます。ブロックは、メソッドをオーバーライドせずに、インタフェースを実装するすべてのクラスにデフォルトの実装を提供します。 デフォルト・メソッドは、クラスで宣言される具象メソッド(§8.4.3.1)と、継承もオーバーライドもされないprivateインタフェース・メソッドとは異なります。

インタフェースは、特定のオブジェクトを参照せずに呼び出されるstaticメソッドを宣言できます。staticインタフェース・メソッドは、デフォルト・メソッド、abstractインタフェース・メソッドおよび非static privateインタフェース・メソッドとは異なります。これらはすべてインスタンス・メソッドです。

staticインタフェース・メソッドの宣言では、静的コンテキスト(§8.1.3)が導入され、現在のオブジェクトを参照する構造体の使用が制限されます。 特に、キーワードthisおよびsuperは、静的コンテキスト(§15.8.3§15.11.2)では禁止されており、字句的に包含する宣言のインスタンス変数、インスタンス・メソッドおよび型パラメータへの修飾されていない参照です(§6.5.5.1§6.5.6.1§15.12.3)。

静的コンテキストまたはネストされたクラスまたはインタフェースからのインスタンス・メソッドへの参照は制限されています(§15.12.3)。

インタフェース・メソッド宣言のstrictfp修飾子は廃止されており、新しいコードでは使用しないでください。 その有無は、実行時に影響しません。

privatedefaultまたはstatic修飾子がないインタフェース・メソッドは、暗黙的にabstractです。 本文はブロックではなくセミコロンで表されます。 スタイルの問題として、このようなメソッド宣言にabstract修飾子を重複して指定することは許可されますが、お薦めしません。

インタフェース・メソッドは、protectedまたはパッケージ・アクセス、あるいは修飾子finalsynchronizedまたはnativeを使用して宣言することはできません。

同じキーワードがインタフェース・メソッド宣言の修飾子として複数回出現した場合、またはインタフェース・メソッド宣言に複数のアクセス修飾子publicおよびprivate (§6.6)がある場合、コンパイル時にエラーが発生します。

インタフェース・メソッド宣言に複数のキーワードabstractdefaultまたはstaticがある場合、コンパイル時にエラーが発生します。

キーワードprivateを含むインタフェース・メソッド宣言にキーワードabstractまたはdefaultも含まれている場合、コンパイル時にエラーが発生します。 インタフェース・メソッド宣言にprivatestaticの両方を含めることが許可されます。

キーワードabstractを含むインタフェース・メソッド宣言にキーワードstrictfpも含まれている場合、コンパイル時にエラーが発生します。

インタフェース宣言の本体で、オーバーライド等価のシグネチャ(§8.4.2)を持つ2つのメソッドを明示的に、または暗黙的に宣言するのはコンパイル時エラーです。 ただし、インタフェースは、このようなシグネチャを持つ複数のabstractメソッドを継承する場合があります(§9.4.1)。

インタフェースで宣言されたメソッドはジェネリックである場合があります。 インタフェース内の汎用メソッドの型パラメータのルールは、クラス内の汎用メソッドの規則と同じです(§8.4.4)。

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

インタフェースIは、その直接スーパーインタフェース・タイプから継承し、すべてのabstractおよびデフォルト・メソッドmに対して、次のすべてに該当します。

  • mは、直接スーパーインタフェース型IJのメンバーです。

  • Iで宣言されたメソッドには、Jのメンバーとしてのmのシグネチャのサブシグネチャ(§8.4.2)を持つシグネチャがありません。

  • IJ' (J'とは異なるm'J)の直接スーパーインタフェースのメンバーであるメソッドm'が存在しないため、m'はメソッドm (§9.4.1.1)の宣言をJ'のインタフェースからオーバーライドします。

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

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

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

interface Bottom extends Left, Right {}

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

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

インタフェースIprivateまたはstaticメソッドmを宣言し、mのシグネチャがスーパーインタフェース型Ipublicインスタンス・メソッドm'のサブシグネチャで、m'Iのコードからアクセス可能である場合、コンパイル時にエラーが発生します。

基本的に、インタフェースのstaticメソッドは、スーパーインタフェース型のインスタンス・メソッドを非表示にできません。 これは、クラス内のstaticメソッドがスーパークラス型またはスーパーインタフェース型のインスタンス・メソッドを非表示にできない§8.4.8.2のルールと似ています。 §8.4.8.2のルールは「staticメソッドを宣言または継承する」クラスを語るのに対し、前述のルールは「staticメソッドを宣言する」インタフェースのみを語ることに注意してください。これは、インタフェースがstaticメソッドを継承できないためです。 また、§8.4.8.2のルールでは、スーパークラス/スーパーインタフェースでインスタンス・メソッドとstaticメソッドの両方を非表示にできますが、前述のルールでは、スーパーインタフェース・タイプのpublicインスタンス・メソッドのみが考慮されます。

同じ行で、インタフェース内のprivateメソッドは、スーパーインタフェース型のインスタンス・メソッド(publicまたはprivate)をオーバーライドできません。 これは、§8.4.8.1および§8.4.8.3のルールに似ています。このルールでは、クラス内のprivateメソッドがスーパークラス型またはスーパーインタフェース型のインスタンス・メソッドをオーバーライドできません。これは、§8.4.8.1ではオーバーライドされたメソッドが非privateである必要があり、§8.4.8.3ではオーバーライドされたメソッドが、オーバーライドされたメソッドと少なくとも同じ数のアクセスを提供する必要があるためです。 要約すると、インタフェース内のpublicメソッドのみをオーバーライドでき、サブインタフェースまたは実装クラスのpublicメソッドによってのみオーバーライドできます。

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

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

  • Iは、Jのサブインタフェースである。

  • ImJを継承しません。

  • mIのシグネチャ(§8.4.2)は、Jという名前のIのスーパータイプのメンバーとしてのmJのシグネチャのサブシグネチャ(§8.4.2)です。

  • mJpublicです。

strictfp修飾子の有無は、メソッドをオーバーライドするルールにはまったく影響しません。 たとえば、strictfp以外のメソッドがstrictfpメソッドをオーバーライドすることは許可され、strictfpメソッドがstrictfp以外のメソッドをオーバーライドすることは許可されます。

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

9.4.1.2. 上書き時の要件

インタフェース・メソッドの戻り型とオーバーライドされたインタフェース・メソッドの戻り型の関係は、§8.4.8.3で規定されています。

インタフェース・メソッドのthrows句とオーバーライドされたインタフェース・メソッドのthrows句の関係は、§8.4.8.3で指定します。

インタフェース・メソッドのシグネチャとオーバーライドされたインタフェース・メソッドのシグネチャの関係は、§8.4.8.3で規定されています。

インタフェース・メソッドのアクセシビリティとオーバーライドされたインタフェース・メソッドのアクセシビリティの関係は、§8.4.8.3に記載されています。

デフォルト・メソッドがクラスObjectprivate以外のメソッドとオーバーライド等価(§8.4.2)の場合、コンパイル時にエラーが発生します。これは、インタフェースを実装するクラスがメソッドの独自の実装を継承するためです。

Objectメソッドの1つをデフォルト・メソッドとして宣言することは、驚くべきことかもしれません。 結局のところ、toStringequalsの動作が正確に定義されるjava.util.Listのようなケースがあります。 しかし、より広範な設計決定が理解されると、動機はより明確になります。

  • まず、スーパークラスから継承されたメソッドは、スーパーインタフェースから継承されたメソッドをオーバーライドできます(§8.4.8.1)。 したがって、すべての実装クラスによって、インタフェースのtoStringデフォルトが自動的にオーバーライドされます。 これは、Javaプログラミング言語での長年の動作です。 これは、デフォルト・メソッドの設計で変更するようなものではありません。これは、インタフェースが絶対に進化することを許可するという目標と競合するためであり、クラスがクラス階層をまだ持っていない場合のみ、デフォルトの動作を提供することになります。

  • 次に、インタフェースはObjectから継承しないが、Object (§9.2)と同じメソッドの多くを暗黙的に宣言します。 したがって、Objectで宣言されたtoStringおよびインタフェースで宣言されたtoStringに共通の祖先はありません。 もっとも、両方がクラスによる継承の候補である場合は、競合します。 この問題を回避するには、クラスおよびインタフェース継承ツリーの複雑なコミングが必要になります。

  • 第3に、インタフェースでObjectメソッドを宣言するユース・ケースは、通常、線形インタフェース階層を想定します。この機能は、多重継承シナリオではあまり一般化されません。

  • 4つ目は、Objectメソッドが非常に基本的なため、任意のスーパーインタフェースが動作を変更するデフォルト・メソッドをサイレントに追加できるようにするのは危険です。

ただし、インタフェースは、Objectメソッドをオーバーライドするクラスに役立つ動作を提供する別のメソッドを定義するために自由です。 たとえば、java.util.Listインタフェースは、toStringの規約によって記述された文字列を生成するelementStringメソッドを宣言できます。クラス内のtoStringの実装者は、このメソッドに委任できます。

9.4.1.3. Override-Equivalentシグネチャを使用したメソッドの継承

インタフェースは、オーバーライド等価のシグネチャを持つ複数のメソッドを継承できます(§8.4.2)。

インタフェース Iが、Iによって継承された別のメソッドとオーバーライド等価の署名を持つデフォルトメソッドを継承すると、コンパイル時にエラーが発生します。 (これは、他のメソッドがabstractdefaultかです。)

それ以外の場合、継承されたすべてのメソッドはabstractで、インタフェースはすべてのメソッドを継承するとみなされます。

継承されたメソッドの1つが、他のすべての継承されたメソッドに対してreturn-type-substitutableである必要があります。そうでない場合、コンパイル時にエラーが発生します。 (この場合、throws句によってエラーは発生しません。)

同じメソッド宣言がインタフェースから継承される複数のパスが存在する場合があります。 この事実は難しくなく、それ自体ではコンパイル時にエラーが発生することはありません。

当然、一致するシグネチャを持つ2つの異なるデフォルト・メソッドがサブインタフェースに継承されると、動作の競合が発生します。 コンクリート・クラスのコンパイル時に問題が発生するのを待たずに、この競合を積極的に検出し、エラーでプログラマに通知します。 このエラーを回避するには、競合するすべてのメソッドをオーバーライドして継承しないようにする新しいメソッドを宣言します。

同様に、abstractメソッドと、一致するシグネチャを持つdefaultメソッドがサブインタフェースに継承されると、エラーが発生します。 この場合、どちらか一方に優先順位を付けることができます。デフォルト・メソッドがabstractメソッドに適切な実装を提供すると想定します。 ただし、これは危険です。偶発的な名前と署名以外では、デフォルト・メソッドがabstractメソッドのcontractと一貫して動作すると信じる理由はありません。サブインタフェースが最初に開発されたときにデフォルト・メソッドが存在していなかった可能性があります。 この状況では、デフォルトの実装が(オーバーライドする宣言を介して)適切であると積極的に主張するようユーザーに求める方が安全です。

これに対して、クラスで継承された具象メソッドの長年の動作は、インタフェースで宣言されたabstractメソッドをオーバーライドすることです(§8.4.8を参照)。 潜在的な契約違反に関する同じ議論がここで適用されますが、この場合、クラスとインタフェースの間に固有の不均衡があります。 クラス階層の独立した性質を維持するために、具体的なメソッドに優先順位を付けるだけでクラスとインタフェースの衝突を最小限に抑えることを推奨します。

9.4.2. オーバーロード

インタフェースの2つのメソッド(同じインタフェースで宣言されたメソッド、またはインタフェースによって継承されたメソッド、または宣言されたメソッドと継承されたメソッドのどちらか)が同じ名前を持つが、オーバーライド等価ではない異なるシグネチャ(§8.4.2)がある場合、メソッド名はオーバーロードされたと見なされます。

この事実は難しくなく、それ自体ではコンパイル時にエラーが発生することはありません。 戻り型間、または2つのメソッドのthrows句間で、同じ名前を持つが、オーバーライド等価ではない異なるシグネチャの間には、必要な関係はありません。

例9.4.2-1 abstractメソッド宣言のオーバーロード

interface PointInterface {
    void move(int dx, int dy);
}
interface RealPointInterface extends PointInterface {
    void move(float dx, float dy);
    void move(double dx, double dy);
}

ここでは、moveという名前のメソッドは、3つの異なるシグネチャ(そのうちの2つが宣言され、1つが継承される)を持つインタフェースRealPointInterfaceでオーバーロードされます。 インタフェースRealPointInterfaceを実装するabstract以外のクラスは、3つのメソッド・シグネチャすべてを実装する必要があります。


9.4.3.  インタフェース・メソッド本体

デフォルト・メソッドにはブロック本文があります。 このコード・ブロックは、クラスがインタフェースを実装するが、メソッドの独自の実装を提供しない場合に、メソッドの実装を提供します。

privateまたはstaticインタフェース・メソッドには、メソッドの実装を提供するブロック本体もあります。

インタフェース・メソッド宣言がabstract (明示的または暗黙的)であり、その本体のブロックがある場合、コンパイル時にエラーが発生します。

インタフェース・メソッド宣言がdefaultprivateまたはstaticで、本体にセミコロンがある場合、コンパイル時にエラーが発生します。

メソッド本体内のreturn文のルールは、§14.17で指定します。

メソッドが戻り型(§8.4.5)を持つように宣言されている場合、メソッドの本文が正常に完了できるとコンパイル時エラーが発生します(§14.1)。

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

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

インタフェース宣言の本体のすべてのメンバー・クラスまたはインタフェース宣言は、暗黙的にpublicおよびstatic (§9.1.1.3)です。 これらの修飾子のいずれかまたは両方を冗長に指定できます。

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

インタフェース宣言の本体におけるメンバー・クラス宣言の修飾子に関する規則は、§8.1.1で規定されています。

インタフェース宣言の本体におけるメンバー・インタフェース宣言の修飾子のルールは、§9.1.1で指定します。

インタフェースが特定の名前を持つメンバー・クラスまたはインタフェースを宣言する場合、メンバー・クラスまたはインタフェースの宣言は、インタフェースのスーパーインタフェースで同じ名前を持つメンバー・クラスおよびインタフェースの、アクセス可能なすべての宣言を非表示にします。

インタフェースは、その直接スーパーインタフェースから、インタフェース内の宣言によって非表示にされない直接スーパーインタフェースのすべてのメンバー・クラスおよびインタフェースを継承します。

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

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

9.6.  注釈インタフェース

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

AnnotationInterfaceDeclaration:

アットマーク(@)とキーワードinterfaceは異なるトークンであることに注意してください。 これらは空白で区切ることができますが、スタイルによってはお薦めしません。

このセクションとそのサブセクションで明示的に変更されていないかぎり、通常のインタフェース宣言(§9.1)に適用されるすべてのルールは、注釈インタフェース宣言に適用されます。

たとえば、注釈インタフェース宣言は、通常のインタフェース宣言と同じスコープのルールを持ちます。

注釈インタフェース宣言に修飾子sealedまたはnon-sealed (§9.1.1.4)が含まれている場合は、コンパイル時にエラーが発生します。

注釈インタフェース宣言では、最上位のインタフェースまたはメンバー・インタフェースを指定できますが、ローカル・インタフェース(§14.3)は指定できません。

注釈インタフェース宣言は、§14.3LocalClassOrInterfaceDeclaration本番により、構文的にブロック内に記述することはできません。

注釈インタフェース宣言がローカル・クラス、ローカル・インタフェースまたは匿名クラス宣言(§14.3§15.9.5)の本体に直接または間接的に出現した場合、コンパイル時にエラーが発生します。

この規則は、前述の注釈インタフェース宣言の構文上の制限とともに、注釈インタフェースが常に正規名(§6.7)を持つことを保証します。 注釈インタフェースの目的は他のコンパイル・ユニットの注釈によって使用されることであるため、そのような名前を持つことは重要です。 ローカル・クラスまたはインタフェースには正規名がないため、その構文本体内のどこかで宣言された注釈インタフェースも(許可されている場合)、正規名を持ちません。

次のコードは、このルールおよび関連する構文的制限の効果を示しています。

class C {
    @interface A1 {}  /* Legal: an annotation interface can be a
                         member interface */

    void m() {
        @interface A2 {}  /* Illegal: an annotation interface cannot
                             be a local interface */

        class D {
            @interface A3 {}  /* Illegal: an annotation interface
                                 cannot be specified anywhere within
                                 the body of local class D */

            class E {
                @interface A4 {}
                  /* Illegal: an annotation interface cannot be
                     specified anywhere within the body of local class
                     D, even as a member of a class E nested in D */
            }
        }
    }
}

注釈インタフェースは決して汎用ではありません(§9.1.2)。

通常のインタフェース宣言とは異なり、注釈インタフェース宣言では、AnnotationTypeDeclaration本番により型変数を宣言できません。

注釈インタフェースの直接スーパーインタフェース型は、常にjava.lang.annotation.Annotation (§9.1.3)です。

通常のインタフェース宣言とは異なり、注釈インタフェース宣言では、AnnotationTypeDeclaration本番により、extends句を介して直接スーパーインタフェース型を選択できません。

注釈インタフェース宣言がextendsを介してスーパーインタフェース型を明示的に指定しないという事実の結果、サブインタフェースの宣言では必ずしもextends句が使用されるため、注釈インタフェースのサブインタフェース自体は注釈インタフェースではありません。 同様に、java.lang.annotation.Annotation自体は注釈インタフェースではありません。

注釈インタフェースは、Objectのインスタンス・メソッド(§9.2)に対応する暗黙的に宣言されたメソッドを含む、java.lang.annotation.Annotationから複数のメソッドを継承しますが、これらのメソッドは注釈インタフェースの要素を定義しません(§9.6.1)。

これらのメソッドはアノテーション・インタフェースの要素を定義しないため、アノテーション・インタフェース(§9.7)に準拠したアノテーションでそれらを使用することは不正です。 このルールを使用しないと、要素が注釈で表現可能な型であること、またはそのアクセッサ・メソッドが使用可能であることを確認できませんでした。

9.6.1.  注釈インタフェース要素

アノテーション・インタフェース宣言の本体には、それぞれがアノテーション・インタフェースの要素を定義するメソッド宣言を含めることができます。 アノテーション・インタフェースには、アノテーション・インタフェース宣言で明示的に宣言されたメソッドによって定義された要素以外の要素はありません。

AnnotationInterfaceBody:
AnnotationInterfaceElementDeclaration:
AnnotationInterfaceElementModifier:
(いずれか)
注釈 public
abstract

ここでは、便宜上、§4.3の次の本番環境を示します。

ディメンション:
{注釈} [ ] {{注釈} [ ]}

前述の文法により、注釈インタフェース宣言のメソッド宣言に仮パラメータ、型パラメータまたはthrows句を含めることはできず、privatedefaultまたはstaticにすることもできません。 したがって、注釈インタフェースは、通常のインタフェースと同じ種類のメソッドを持つことはできません。 Java SE 26では、注釈インタフェースが暗黙的なスーパーインタフェースjava.lang.annotation.Annotationからデフォルト・メソッドを継承することは可能ですが、このようなデフォルト・メソッドは存在しません。

規則によって、注釈インタフェース要素の宣言に存在する必要がある修飾子は、注釈のみです。

注釈インタフェースの本体で宣言されたメソッドの戻り型は、次のいずれかである必要があります。そうしないと、コンパイル時にエラーが発生します。

  • プリミティブ型

  • String

  • ClassまたはClassの呼出し(§4.5)

  • enumクラス型

  • 注釈インタフェース型

  • コンポーネント・タイプが前述の型の1つである配列タイプ(§10.1)。

このルールでは、次のようなネストされた配列型を持つ要素は除外されます:

@interface Verboten {
    String[][] value();
}

配列を返すメソッドの宣言では、配列型を示す大カッコのペアを空の仮パラメータ・リストの後に配置できます。 この構文は、Javaプログラミング言語の初期バージョンとの互換性のためにサポートされています。 この構文は新しいコードでは使用しないことを強くお薦めします。

注釈インタフェースで宣言されたメソッドに、クラスObjectまたはインタフェースjava.lang.annotation.Annotationで宣言されたpublicまたはprotectedメソッドのシグネチャとオーバーライド等価(§8.4.2)のシグネチャがある場合、コンパイル時にエラーが発生します。

注釈インタフェース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 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および要素を定義するメソッド宣言に値をアタッチすることによって指定されるデフォルト値を指定できます。

DefaultValue:
defaultElementValue

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

ElementValueArrayInitializer:
{ [ElementValueList] [,] }
ElementValueList:

デフォルト値を持つように指定されている注釈インタフェース要素は、デフォルト・メソッド(§9.4)ではないことに注意してください。 注釈インタフェースの宣言では、デフォルト・メソッドを宣言できません(§9.6.1)。

要素の型が、指定されたデフォルト値と同等でない場合(§9.7)、コンパイル時にエラーが発生します。

デフォルト値は注釈にコンパイルされず、注釈の読取り時に動的に適用されます。 したがって、デフォルト値を変更すると、変更が(これらの注釈にデフォルト要素の明示的な値がないと仮定)に行われる前にコンパイルされたクラスであっても注釈に影響します。

例9.6.2-1. デフォルト値を使用した注釈インタフェース宣言

§9.6.1RequestForEnhancement注釈インタフェースの絞込みを次に示します。

@interface RequestForEnhancement {
    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. 繰返し可能な注釈インタフェース

注釈インタフェースA反復可能になるのは、その宣言が@Repeatable注釈(§9.6.4.8)で注釈付けされている(メタ)場合で、value要素がAの注釈インタフェースを含むことを示す場合です。

次のすべてに当てはまる場合、注釈インタフェースACAの注釈インタフェースを含むです。

  1. ACは、戻り型がA[]value()メソッドを宣言します。

  2. ACで宣言されたvalue()以外のメソッドは、デフォルト値を持ちます。

  3. ACは、A (@Retentionアノテーション(§9.6.4.2)を使用して、保持が明示的にまたは暗黙的に表される)まで、少なくとも保持されます。 具体的には、次のようになります。

    • ACの保持がjava.lang.annotation.RetentionPolicy.SOURCEの場合、Aの保持はjava.lang.annotation.RetentionPolicy.SOURCEです。

    • ACの保持がjava.lang.annotation.RetentionPolicy.CLASSの場合、Aの保持はjava.lang.annotation.RetentionPolicy.CLASSまたはjava.lang.annotation.RetentionPolicy.SOURCEです。

    • ACの保持がjava.lang.annotation.RetentionPolicy.RUNTIMEの場合、Aの保持はjava.lang.annotation.RetentionPolicy.SOURCEjava.lang.annotation.RetentionPolicy.CLASSまたはjava.lang.annotation.RetentionPolicy.RUNTIMEです。

  4. Aは、AC (§9.6.4.1)と少なくとも同じ種類のプログラム要素に適用できます。 具体的には、Aが適用可能なプログラム要素の種類がセットm1で示され、ACが適用可能なプログラム要素の種類がセットm2で示される場合、m2の各種類がm1で発生する必要があります。ただし、次の点が異なります。

    • m2内の種類がjava.lang.annotation.ElementType.ANNOTATION_TYPEの場合、m1java.lang.annotation.ElementType.ANNOTATION_TYPEjava.lang.annotation.ElementType.TYPEまたはjava.lang.annotation.ElementType.TYPE_USEの少なくとも1つが存在する必要があります。

    • m2の種別がjava.lang.annotation.ElementType.TYPEの場合、m1に少なくとも1つのjava.lang.annotation.ElementType.TYPEまたはjava.lang.annotation.ElementType.TYPE_USEが存在する必要があります。

    • m2の種別がjava.lang.annotation.ElementType.TYPE_PARAMETERの場合、m1に少なくとも1つのjava.lang.annotation.ElementType.TYPE_PARAMETERまたはjava.lang.annotation.ElementType.TYPE_USEが存在する必要があります。

    この句は、注釈インタフェースが適用可能なプログラム要素の一部のみに対して繰返し可能である可能性があるというポリシーを実装します。

  5. Aの宣言にjava.lang.annotation.Documentedに対応する(メタ)注釈がある場合、ACの宣言には、java.lang.annotation.Documentedに対応する(メタ)注釈が必要です。

    A@Documentedでない場合、AC@Documentedになることができます。

  6. Aの宣言にjava.lang.annotation.Inheritedに対応する(メタ)注釈がある場合、ACの宣言には、java.lang.annotation.Inheritedに対応する(メタ)注釈が必要です。

    A@Inheritedでない場合、AC@Inheritedになることができます。

注釈インタフェースAが、value要素がAの包含注釈インタフェースではない型を示す@Repeatable注釈で(メタ)注釈付けされている場合、コンパイル時にエラーが発生します。

例9.6.3-1. 不整形式の包含注釈インタフェース

次の宣言について考えてみます:

import java.lang.annotation.Repeatable;

@Repeatable(FooContainer.class)
@interface Foo {}

@interface FooContainer { Object[] value(); }

Foo@Repeatableを使用してFooContainerを包含する注釈インタフェースとして指定しようとしますが、実際にはFooContainerFooの包含する注釈インタフェースではないため、Foo宣言をコンパイルするとコンパイル時にエラーが発生します。 (FooContainer.value()の戻り型はFoo[]ではありません。)


@Repeatableアノテーションを繰り返すことはできないため、繰返し可能なアノテーション・インタフェースで指定できるアノテーション・インタフェースは1つのみです。

複数の包含アノテーション・インタフェースを指定できるようにすると、繰返し可能なアノテーション・インタフェースの複数のアノテーションがコンテナ・アノテーション(§9.7.5)で論理的に置換される場合に、コンパイル時に望ましくない選択が生じます。

注釈インタフェースは、最大1つの注釈インタフェースの包含注釈インタフェースにできます。

これは、注釈インタフェースAの宣言でACの包含注釈インタフェースが指定されている場合、ACvalue()メソッドにはA (特にA[])を含む戻り型があるという要件によって暗示されます。

注釈インタフェースは、それ自体を含む注釈インタフェースとして指定できません。

これは、包含アノテーション・インタフェースのvalue()メソッドの要件によって示されます。 具体的には、注釈インタフェースAがそれ自身を(@Repeatableを介して)包含注釈インタフェースとして指定した場合、Avalue()メソッドの戻り型はA[]である必要がありますが、注釈インタフェースがその要素(§9.6.1)でそれ自体を参照できないため、コンパイル時にエラーが発生します。 より一般的には、2つの注釈インタフェースは、循環注釈インタフェース宣言が不正であるため、包含注釈インタフェースとして指定できません。

注釈インタフェースACは、一部の注釈インタフェースAの包含注釈インタフェースであり、独自の包含注釈インタフェースSCを持つ場合もあります。 つまり、包含アノテーション・インタフェース自体が繰返し可能なアノテーション・インタフェースである場合があります。

例9.6.3-2  注釈を繰り返すことができる場所の制限

java.lang.annotation.ElementType.TYPEのターゲットを示すインタフェース宣言を持つ注釈は、少なくともjava.lang.annotation.ElementType.ANNOTATION_TYPEのターゲットを示すインタフェース宣言を持つ注釈と同じ数の場所に存在できます。 たとえば、繰返し可能で注釈インタフェースを含む次の宣言があるとします。

import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Target;

@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がメソッド宣言で繰り返されないようにする場合)、FooContainerFooの包含注釈インタフェースとして正当に機能します。 ただし、FooContainerが仮パラメータ宣言にのみ適用可能な場合、@Fooが繰り返される一部のプログラム要素では@FooContainerを暗黙的に宣言できないため、FooContainerFooによってアノテーション・インタフェースを含めることの選択が不十分でした。

    同様に、Fooがフィールド宣言およびメソッド宣言に適用可能な場合、FooContainerがフィールド宣言およびパラメータ宣言に適用可能な場合、FooContainerは正当にFooの包含注釈インタフェースとして機能できません。 プログラム要素の交差を取得し、フィールド宣言でのみFooを繰り返し可能にすることは可能ですが、FooContainerに追加のプログラム要素が存在することは、FooContainerFooの包含注釈インタフェースとして設計されていないことを示します。 したがって、Fooが依存するのは危険です。


例9.6.3-3. 繰返し可能な包含注釈インタフェース

次の宣言は有効です:

import java.lang.annotation.Repeatable;

// Foo: Repeatable annotation interface
@Repeatable(FooContainer.class)
@interface Foo { int value(); }

// FooContainer: Containing annotation interface of Foo
// Also a repeatable annotation interface itself
@Repeatable(FooContainerContainer.class)
@interface FooContainer { Foo[] value(); }

// FooContainerContainer: Containing annotation interface
// of FooContainer
@interface FooContainerContainer { FooContainer[] value(); }

したがって、インタフェースが包含アノテーション・インタフェースであるアノテーションは、それ自体を繰り返すことができます。

@FooContainer({@Foo(1)}) @FooContainer({@Foo(2)})
class Test {}

繰返し可能で包含される注釈インタフェースは、繰返し可能な注釈インタフェースのアノテーションと包含する注釈インタフェースのアノテーション(§9.7.5)の混在に関するルールに従います。 たとえば、複数の@Fooアノテーションを複数の@FooContainerアノテーションとともに記述することも、複数の@FooContainerContainerアノテーションとともに複数の@FooContainerアノテーションを記述することもできません。 ただし、注釈インタフェースFooContainerContainer自体が反復可能であった場合は、複数の@FooContainerContainer注釈とともに複数の@Foo注釈を記述できます。


9.6.4.  事前定義済注釈インタフェース

Java SE Platform APIには、いくつかの注釈インタフェースが事前定義されています。 事前定義された注釈インタフェースの中には、Javaプログラミング言語で特別なセマンティクスを持つものがあり、この項で説明するように、Javaコンパイラの部分で特別な動作を必要とします。 この項では、Java SE Platform APIドキュメント(§1.4)を参照する事前定義済注釈インタフェースの完全な仕様は提供していません。

9.6.4.1. @Target

java.lang.annotation.Target型の注釈は、注釈インタフェースAの宣言で使用され、A適用可能であるコンテキストを指定します。java.lang.annotation.Targetには、java.lang.annotation.ElementType[]型の単一要素valueがあり、コンテキストを指定します。

注釈インタフェースは、宣言に注釈が適用される宣言コンテキスト、または宣言および式で使用される型に注釈が適用される型コンテキストで適用できます。

宣言コンテキストは10個あり、それぞれjava.lang.annotation.ElementTypeのenum定数に対応しています。

  1. モジュール宣言(§7.7)

    java.lang.annotation.ElementType.MODULEに対応します。

  2. パッケージ宣言(§7.4.1)

    java.lang.annotation.ElementType.PACKAGEに対応します。

  3. クラス宣言(enum宣言およびレコード宣言を含む)およびインタフェース宣言(注釈インタフェース宣言を含む)(§8.1.1§8.5§8.9§8.10§9.1.1§9.5§9.6)

    java.lang.annotation.ElementType.TYPEに対応します。

    さらに、注釈インタフェース宣言はjava.lang.annotation.ElementType.ANNOTATION_TYPEに対応しています。

  4. メソッド宣言(注釈インタフェースの要素を含む)(§8.4.3§9.4§9.6.1)

    java.lang.annotation.ElementType.METHODに対応します。

  5. コンストラクタ宣言(§8.8.3)

    java.lang.annotation.ElementType.CONSTRUCTORに対応します。

  6. 汎用クラス、インタフェース、メソッドおよびコンストラクタの型パラメータ宣言(§8.1.2§9.1.2§8.4.4§8.8.4)

    java.lang.annotation.ElementType.TYPE_PARAMETERに対応します。

  7. フィールド宣言(enum定数を含む)(§8.3.1§9.3§8.9.1)

    java.lang.annotation.ElementType.FIELDに対応します。

  8. 公式および例外パラメータ宣言(§8.4.1§9.4§14.20)

    java.lang.annotation.ElementType.PARAMETERに対応します。

  9. 文(§14.4.2§14.14.1§14.14.2§14.20.3)およびパターン(§14.30.1)でのローカル変数宣言

    java.lang.annotation.ElementType.LOCAL_VARIABLEに対応します。

  10. レコード・コンポーネントの宣言(§8.10.1)

    java.lang.annotation.ElementType.RECORD_COMPONENTに対応します。

17種類のコンテキスト(§4.11)があり、すべてjava.lang.annotation.ElementTypeのenum定数TYPE_USEで表されます。

同じenum定数がjava.lang.annotation.Target型の注釈のvalue要素に複数回出現する場合は、コンパイル時にエラーが発生します。

注釈インタフェースAの宣言にjava.lang.annotation.Target型の注釈が存在しない場合、Aはすべての宣言コンテキストに適用でき、型コンテキストには適用されません。

9.6.4.2. @Retention

注釈は、ソース・コードにのみ存在することも、クラスまたはインタフェースのバイナリ形式で存在することもできます。 バイナリ形式の注釈は、実行時にJava SEプラットフォームのリフレクション・ライブラリを介して使用できる場合とできない場合があります。 注釈インタフェースjava.lang.annotation.Retentionは、これらのいずれかを選択するために使用されます。

注釈aが注釈インタフェースAに対応し、Ajava.lang.annotation.Retentionに対応する(メタ)注釈mがある場合、次のようになります。

  • mに値がjava.lang.annotation.RetentionPolicy.SOURCEの要素がある場合、Javaコンパイラは、aが出現するクラスまたはインタフェースのバイナリ表現にaが存在しないことを確認する必要があります。

  • mjava.lang.annotation.RetentionPolicy.CLASSまたはjava.lang.annotation.RetentionPolicy.RUNTIMEの値を持つ要素がある場合、Javaコンパイラは、aがローカル変数宣言に注釈を付けたり、aがラムダ式の仮パラメータ宣言に注釈を付けたりしないかぎり、aが出現するクラスまたはインタフェースのバイナリ表現でaが表されるようにする必要があります。

    ローカル変数の宣言またはラムダ式の仮パラメータの宣言に関する注釈は、バイナリ表現に保持されません。 一方、ローカル変数の型、またはラムダ式の仮パラメータの型に関する注釈は、注釈インタフェースで適切な保存ポリシーが指定されている場合、バイナリ表現に保持されます。

    注釈インタフェースが@Target(java.lang.annotation.ElementType.LOCAL_VARIABLE)および@Retention(java.lang.annotation.RetentionPolicy.CLASS)または@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)でメタ注釈付けされることは違法ではありません。

    mに値がjava.lang.annotation.RetentionPolicy.RUNTIMEの要素がある場合、Java SEプラットフォームのリフレクション・ライブラリは、実行時にaを使用可能にする必要があります。

Ajava.lang.annotation.Retentionに対応する(メタ)注釈がない場合、JavaコンパイラはAを、値がjava.lang.annotation.RetentionPolicy.CLASSの要素を持つjava.lang.annotation.Retentionに対応する(メタ)注釈があるかのように処理する必要があります。

9.6.4.3. @Inherited

注釈インタフェースjava.lang.annotation.Inheritedは、特定の注釈インタフェースに対応するクラスCの注釈がCのサブクラスによって継承されることを示すために使用されます。

9.6.4.4. @Override

プログラマは、メソッド宣言をオーバーライドすることを意味する場合にメソッド宣言をオーバーロードすることがあり、微妙な問題が発生します。 注釈インタフェースOverrideは、このような問題の早期検出をサポートします。

典型的な例は、equalsメソッドに関するものです。 プログラマは、クラスFooに次のように記述します。

public boolean equals(Foo that) { ... }

書込みを意味する場合:

public boolean equals(Object that) { ... }

これは完全に正当ですが、クラスFooObjectからequals実装を継承し、微妙なバグを引き起こす可能性があります。

クラスまたはインタフェースQのメソッド宣言に@Overrideの注釈が付けられている場合、次の3つの条件のいずれかがtrueである必要があり、そうでないとコンパイル時にエラーが発生します。

  • Q (§8.4.8.1§9.4.1.1)のスーパータイプで宣言されたメソッドを Qからオーバーライドするメソッド

  • このメソッドは、Objectpublicメソッド(§4.3.2§8.4.2)とオーバーライド等価です。

  • Qはレコードクラス(§8.10)で、このメソッドは Q (§8.10.3)のレコードコンポーネントのアクセッサメソッドです。

この動作はJava SE 5.0とは異なります。ただし、スーパークラスにも存在しないスーパーインタフェースからメソッドを実装したメソッドに適用した場合、@Overrideによってコンパイル時エラーが発生しただけです。

Objectpublicメソッドのオーバーライドに関する句は、インタフェースで@Overrideを使用することで動機付けされます。 次の宣言について考えてみます:

class Foo     { @Override public int hashCode() {..} }
interface Bar { @Override int hashCode(); }

Foo.hashCodeはメソッドObject.hashCodeFooからオーバーライドするため、クラス宣言での@Overrideの使用は最初の句で有効です。

インタフェース宣言では、インタフェースにObjectpublicメンバー(§9.2)に対応するpublic abstractメンバーがあることを考慮してください。 インタフェースが明示的に宣言することを選択した場合(つまり、Objectpublicメソッドとオーバーライド等価のメンバーを宣言する場合)、インタフェースはそれらをオーバーライドするとみなされ、@Overrideの使用が許可されます。

ただし、cloneメソッドで@Overrideを使用しようとするインタフェースについて考えてみます(この例ではfinalizeを使用することもできます)。

interface Quux { @Override Object clone(); }

Object.clonepublicではないため、Quuxで暗黙的に宣言されたcloneというメンバーはありません。 したがって、Quuxでのcloneの明示的な宣言は、他のメソッドを「実装」するものとはみなされず、@Overrideを使用することは間違っています。 (Quux.clonepublicであるという事実は関係ありません。)

対照的に、cloneを宣言するクラス宣言は、単にObject.cloneをオーバーライドするため、@Overrideを使用できます。

class Beep { @Override protected Object clone() {..} }

レコード・クラスに関する句は、レコード宣言における@Overrideの特別な意味によるものです。 つまり、メソッド宣言がレコード・コンポーネントのアクセッサ・メソッドであることを指定するために使用できます。 次のレコード宣言を検討してください:

record Roo(int x) {
    @Override
    public int x() {
        return Math.abs(x);
    }
}

アクセッサ・メソッドint x()@Overrideを使用すると、レコード・コンポーネントxが変更または削除された場合に、対応するアクセッサ・メソッドも変更または削除する必要があります。

9.6.4.5. @SuppressWarnings

Javaコンパイラは、役立つ"lint-like"警告を発行できるようになります。 このような警告の使用を推奨するには、プログラマが警告が不適切であることを知っている場合に、プログラムの一部で警告を無効にする方法があります。

注釈インタフェースSuppressWarningsは、Javaコンパイラによって発行された警告に対するプログラマの制御をサポートします。 Stringの配列である単一の要素を定義します。

宣言に@SuppressWarnings(value = {S1, ..., Sk})の注釈が付けられている場合、Javaコンパイラは、S1のいずれかで指定された警告を抑制(つまり、レポートしない)する必要があります。Sk: 注釈付き宣言またはそのいずれかの部分の結果として警告が生成される場合。

Javaプログラミング言語では、@SuppressWarningsで指定できる4種類の警告が定義されています。

その他の文字列では、非標準の警告が指定されます。 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の注釈が付けられている場合でも、ローカル変数または仮パラメータが使用されます(名前で参照されます)。

  • パッケージの宣言に@Deprecatedの注釈が付けられている場合でも、パッケージの名前(修飾型名、import宣言、exportsまたはopensディレクティブで参照)が使用されます。

  • フレンド・モジュールの宣言に@Deprecatedの注釈が付けられている場合でも、修飾されたexportsまたはopensディレクティブによってモジュールの名前が使用されます。

パッケージをエクスポートまたはオープンするモジュール宣言は、通常、パッケージ宣言を制御する同じプログラマまたはチームによって制御されます。 そのため、パッケージがモジュール宣言によってエクスポートまたはオープンされたときに、パッケージ宣言に@Deprecatedの注釈が付けられるという警告の利点はほとんどありません。 一方、友人モジュールにパッケージをエクスポートまたはオープンするモジュール宣言は、通常、友人モジュールを制御する同じプログラマまたはチームによって制御されません。 パッケージをエクスポートまたはオープンするだけでは、モジュール宣言がフレンド・モジュールに依存するわけではないため、フレンド・モジュールが非推奨の場合、警告の値はほとんどありません。モジュール宣言のプログラマは、ほとんどの場合、このような警告を抑制する必要があります。

非推奨の警告または削除の警告を引き起こす可能性のある暗黙的な宣言は、コンテナ注釈(§9.7.5)のみです。 つまり、Tが繰返し可能な注釈インタフェースで、TCがそれに含まれる注釈インタフェースで、TCが非推奨の場合、@T注釈を繰り返すと警告が発生します。 この警告は、暗黙的な@TCコンテナ注釈が原因です。 対応する繰返し可能な注釈インタフェースを非推奨にせずに、包含する注釈インタフェースを非推奨にすることを強くお薦めします。

9.6.4.7. @SafeVarargs

変更不可能な要素型(§4.7)を持つ可変引数パラメータにより、ヒープ汚染(§4.12.2)が発生し、コンパイル時の未チェックの警告(§5.1.9)が発生する可能性があります。 ただし、変数arityメソッドの本体が変数arityパラメータに関して十分に動作している場合、このような警告は非公式です。

アノテーション・インタフェースSafeVarargsは、メソッドまたはコンストラクタ宣言に注釈を付けるために使用されると、プログラマ・アサーションを作成します。これにより、Javaコンパイラは、変数arityメソッドまたはコンストラクタの宣言または呼出しに対して未チェックの警告をレポートできなくなります。この変数arityパラメータは、変更不可能な要素型を持つため、これを行いません。

注釈@SafeVarargsは、変数arityメソッド自体の宣言(§8.4.1)に関する未チェックの警告に加えて、メソッド呼出し式での未チェックの警告を抑制するため、非ローカル効果があります。 対照的に、注釈@SuppressWarnings("unchecked")は、メソッドの宣言に関連する未チェックの警告のみを抑制するため、局所的な効果があります。

@SafeVarargsの正規ターゲットは、java.util.Collections.addAllのようなメソッドで、宣言は次のように始まります。

public static <T> boolean
  addAll(Collection<? super T> c, T... elements)

変数のarityパラメータに宣言された型T[]があり、これは変更できません。 ただし、このメソッドは基本的には入力配列から読み取って要素をコレクションに追加するだけで、どちらも配列に関する安全な操作です。 したがって、java.util.Collections.addAllのメソッド呼出し式でのコンパイル時の未チェックの警告は、間違いなく誤りであり、非情報的です。 @SafeVarargsをメソッド宣言に適用すると、メソッド呼出し式でこれらの未チェックの警告が生成されなくなります。

固定のArityメソッドまたはコンストラクタ宣言に注釈@SafeVarargsが付けられている場合、コンパイル時にエラーが発生します。

staticでもfinalでもprivateでもない変数引数のメソッド宣言に注釈@SafeVarargsが付加されている場合、コンパイル時にエラーが発生します。

@SafeVarargsstaticメソッド、finalインスタンス・メソッドまたはprivateインスタンス・メソッド(あるいはその両方)およびコンストラクタにのみ適用されるため、メソッドのオーバーライドが発生した場合、注釈は使用できません。 注釈の継承は、(メソッド、インタフェースまたはコンストラクタではなく)クラスの注釈に対してのみ機能するため、@SafeVarargs形式の注釈は、クラス内のインスタンス・メソッドまたはインタフェースを介して渡すことはできません。

9.6.4.8. @Repeatable

注釈インタフェースjava.lang.annotation.Repeatableは、繰返し可能注釈インタフェースの宣言で使用され、その包含注釈インタフェース(§9.6.3)を示します。

ACを示すAの宣言に対する@Repeatableメタ注釈は、ACをAの包含注釈インタフェースにするのに十分ではないことに注意してください。 ACには、Aの包含アノテーション・インタフェースとみなされる多数の整形式ルールがあります。

9.6.4.9. @FunctionalInterface

注釈インタフェースFunctionalInterfaceは、インタフェースが関数型インタフェース(§9.8)であることを表すために使用されます。 これにより、機能することを意図したインタフェース内に出現する、またはインタフェースによって継承される不適切なメソッド宣言の早期検出が容易になります。

インタフェース宣言に@FunctionalInterfaceの注釈が付けられているが、実際には関数型インタフェースではない場合、コンパイル時にエラーが発生します。

一部のインタフェースは偶然に機能するため、関数型インタフェースのすべての宣言に@FunctionalInterfaceの注釈を付ける必要も望ましくもありません。

9.7.  注釈

注釈は、情報をプログラム要素に関連付けるマーカーですが、実行時に影響を与えません。 注釈は、注釈インタフェース(§9.6)の特定のインスタンスを示し、通常はそのインタフェースの要素の値を提供します。

3種類の注釈があります。 1つ目の種類が最も一般的で、他の種類は単に1つ目の種類の簡略表現です。

通常の注釈については、§9.7.1§9.7.2のマーカー注釈、および§9.7.3の単一要素注釈で説明します。 注釈は、§9.7.4で説明されているように、プログラム内の様々な構文の場所に表示される場合があります。 場所に出現する可能性のある同じインタフェースのアノテーションの数は、§9.7.5で説明されているインタフェース宣言によって決まります。

9.7.1.  標準注釈

通常の注釈は、注釈インタフェースの名前と、オプションでカンマ区切りの要素と値のペアのリストを指定します。 各ペアには、注釈インタフェースの要素(§9.6.1)に関連付けられた要素値が含まれます。

NormalAnnotation:
ElementValuePairList:
ElementValuePair:
ElementValueArrayInitializer:
{ [ElementValueList] [,] }
ElementValueList:

アットマーク(@)はそれ自体に対するトークンであることに注意してください(§3.11)。 TypeNameとの間に空白を入れることは可能ですが、スタイルの問題としてはお薦めしません。

TypeNameでは、注釈に対応する注釈インタフェースを指定します。 この注釈は、そのインタフェースに「of」と表示されます。

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

要素と値のペアの識別子は、注釈インタフェースのいずれかの要素(つまり、メソッド)の単純な名前である必要があります。そうしないと、コンパイル時にエラーが発生します。

このメソッドの戻り型は、要素と値のペアの要素型を定義します。

要素型が配列型である場合、要素と値のペアの要素値を指定するのに中カッコを使用する必要はありません。 エレメント値がElementValueArrayInitializerでない場合は、唯一のエレメントがエレメント値である配列値がエレメントに関連付けられます。 エレメント値がElementValueArrayInitializerの場合、ElementValueArrayInitializerで表される配列値はそのエレメントに関連付けられます。

要素型が要素値とともに補正されない場合は、コンパイル時にエラーが発生します。 要素型Tは、次のいずれかが当てはまる場合にのみ、要素値vと相乗的になります。

  • Tは配列型E[]で、次のいずれかです。

    • vConditionalExpressionまたはAnnotationの場合、vEと同等です。または

    • vElementValueArrayInitializerの場合、vに含まれる各要素値はEと同等です。

      ElementValueArrayInitializerは、通常の配列イニシャライザ(§10.6)と似ていますが、ElementValueArrayInitializerには、注釈と式およびネストされたイニシャライザを構文的に含めることができます。 ただし、ネストされたイニシャライザはElementValueArrayInitializerでは意味的に有効ではありません。これは、アノテーション・インタフェース宣言の配列型要素と相乗的でないためです(ネストされた配列型は許可されません)。

  • Tは配列型ではなく、vの型はTとの代入互換(§5.2)であり、次のことを行います。

    • Tがプリミティブ型またはStringの場合、vは定数式(§15.29)です。

    • TClassまたはClassの呼出し(§4.5)の場合、vはクラス・リテラル(§15.8.2)です。

    • Tがenumクラス型(§8.9)の場合、vはenum定数(§8.9.1)です。

    • vnullではありません。

Tが配列型または注釈インタフェースでない場合、要素値はConditionalExpression (§15.25)である必要があります。 「式」のようなより一般的な生成ではなく、「ConditionalExpression」を使用することは、代入式を要素値として防止するための構文上のトリックです。 代入式は定数式ではないため、プリミティブ要素またはString型要素の代数要素値にはできません。

通常の注釈には、対応する注釈インタフェースのすべての要素について要素と値のペアを含める必要があります。ただし、デフォルト値を持つ要素を除きます。そうしないと、コンパイル時にエラーが発生します。

標準注釈には、デフォルト値が指定された要素の要素と値のペアを含めることができます(必須ではありません)。

アノテーション内の要素と値のペアは、アノテーション・インタフェース宣言内の対応する要素と同じ順序で提示されることは、必須ではありませんが慣例です。

注釈インタフェース宣言の注釈は、メタ注釈と呼ばれます。

インタフェースAの注釈は、インタフェースA自体の宣言にメタ注釈として表示される場合があります。 より一般的には、「注釈付け」関係の推移閉包の循環性が許可されます。

たとえば、注釈インタフェースSの宣言にインタフェースTのメタ注釈を付け、T自身の宣言にインタフェースSのメタ注釈を付けて注釈を付けることは有効です。 事前定義された注釈インタフェース(§9.6.4)には、このような循環がいくつか含まれています。

例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()

すべての要素にデフォルト値(§9.6.2)があるかぎり、要素を含む注釈インタフェースにマーカー注釈を使用することは有効です。

例9.7.2-1.  マーカー注釈

次に、§9.6.1Preliminaryマーカー注釈インタフェースを使用する例を示します。

@Preliminary public class TimeTravel { ... }

9.7.3.  単一要素注釈

単一要素注釈は、単一要素注釈インタフェース(§9.6.1)で使用するために設計された短縮形です。

SingleElementAnnotation:

これは次の標準注釈の簡略表現です。

@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)に注釈インタフェースが適用される注釈、またはクラス、インタフェースまたは型パラメータ宣言に適用され、その注釈インタフェースが型コンテキスト(§4.11)に適用可能な注釈です。

型注釈は、型(または型の任意の部分)に適用され、その注釈インタフェースが型コンテキストに適用可能な注釈です。

たとえば、フィールド宣言の場合は次のようになります:

@Foo int f;

@Fooは、Foo@Target(ElementType.FIELD)によってメタ注釈付けされている場合はfの宣言注釈であり、Foo@Target(ElementType.TYPE_USE)によってメタ注釈付けされている場合はintの型注釈です。 @Fooを宣言注釈と型注釈の両方に同時に指定できます。

型注釈は、配列型またはその任意のコンポーネント型に適用できます(§10.1)。 たとえば、ABおよびC@Target(ElementType.TYPE_USE)でメタ注釈付けされた注釈インタフェースであると仮定すると、フィールド宣言は次のようになります。

@C int @A [] @B [] f;

@Aは配列型int[][]に適用され、@Bはそのコンポーネント型int[]に適用され、@Cは要素型intに適用されます。 その他の例は、§10.2を参照してください。

この構文の重要なプロパティは、配列レベルの数のみが異なる2つの宣言で、型の左側の注釈が同じ型を参照することです。 たとえば、@Cは、次のすべての宣言でint型に適用されます。

@C int f;
@C int[] f;
@C int[][] f;

これは、特に必要ではなく、他のすべての修飾子の前に宣言の注釈を記述するためであり、適用する型の直前に注釈を入力することです。

注釈は、プログラム内で宣言または型、あるいはその両方に柔軟に適用される構文上のロケーションに表示できます。 これは、宣言されたエンティティの型の直前に修飾子がある6つの宣言コンテキストのいずれかで発生する可能性があります。

  • メソッド宣言(注釈インタフェースの要素を含む)

  • コンストラクタの宣言

  • フィールド宣言(列挙型定数を含む)

  • 仮パラメータと例外パラメータの宣言

  • ローカル変数宣言

  • レコード・コンポーネント宣言

Javaプログラミング言語の文法では、これらの場所にある注釈を宣言の修飾子(§8.3)として明確に扱いますが、これは単なる構文の問題です。 注釈が宣言または宣言されたエンティティの型に適用されるかどうか、つまり、注釈が宣言注釈型注釈かは、注釈のインタフェースの適用性によって異なります。

  • 注釈のインタフェースが、宣言に対応する宣言コンテキストに適用可能であり、型コンテキストには適用可能でない場合、注釈は宣言のみに適用されるとみなされます。

  • 注釈のインタフェースが、型コンテキストに適用可能であり、宣言に対応する宣言コンテキストには適用可能でない場合、注釈はその注釈に最も近い型のみに適用されるとみなされます。

  • 宣言および型コンテキストに対応する宣言コンテキストで注釈のインタフェースが適用可能な場合、注釈は宣言および注釈に最も近い型の両方に適用されるとみなされます。

前述の2番目と3番目のケースでは、注釈に最も近い型は次のように決定されます。

  • 注釈がvoidメソッド宣言またはvarを使用する変数宣言(§14.4§15.27.1)の前に存在する場合、最も近い型はありません。 注釈のインタフェースが、その注釈に最も近い型のみに適用されるとみなされる場合、コンパイル時にエラーが発生します。

  • 注釈がコンストラクタ宣言の前に表示された場合、新たに構築されたオブジェクトの型として最も近い型が使用されます。 新規作成されたオブジェクトの型は、コンストラクタ宣言を即時に囲んでいる型の完全修飾名です。 その完全修飾名の中では、注釈は、コンストラクタ宣言によって示された単純型の名前に適用されます。

  • その他すべての場合において、最も近い型は、宣言されたエンティティのソース・コードで記述された型です。その型が配列型の場合、要素型は注釈に最も近いものとみなされます。

    たとえば、フィールド宣言@Foo public static String f;では、@Fooに最も近い型はStringです。 (フィールド宣言の型がjava.lang.Stringとして記述されている場合、java.lang.String@Fooに最も近い型になり、それ以降のルールでは型注釈がパッケージ名javaに適用されないようになります。) 汎用メソッド宣言@Foo <T> int[] m() {...}では、宣言されたエンティティに書き込まれる型はint[]であるため、@Fooは要素型intに適用されます。

    varを使用しないローカル変数宣言は、ラムダ式の仮パラメータ宣言に似ていますが、ソース・コードでは宣言注釈と型注釈の両方を使用できますが、classファイルに格納できるのは型注釈のみです。

インタフェースAの注釈が構文的に次の修飾子である場合、コンパイル時にエラーが発生します。

  • モジュール宣言。ただし、Aはモジュール宣言に適用できません。

  • パッケージ宣言。ただし、Aはパッケージ宣言に適用できません。

  • クラスまたはインタフェース宣言ですが、Aは型宣言または型コンテキストには適用できません。

    注釈インタフェース宣言ですが、Aは注釈インタフェース宣言、型宣言、または型コンテキストには適用できません。

  • メソッド宣言(注釈インタフェースの要素を含む)ですが、Aはメソッド宣言または型コンテキストには適用できません。

  • コンストラクタ宣言ですが、Aはコンストラクタ宣言または型コンテキストには適用できません。

  • 汎用クラス、インタフェース、メソッドまたはコンストラクタの型パラメータ宣言ですが、Aは型パラメータ宣言または型コンテキストには適用できません。

  • フィールド宣言(またはenum定数)ですが、Aはフィールド宣言または型コンテキストには適用できません。

  • 仮パラメータ宣言または例外パラメータ宣言ですが、Aは仮パラメータ宣言および例外パラメータ宣言または型コンテキストには適用できません。

  • 受信者パラメータですが、Aは型コンテキストでは適用されません。

  • 文またはパターンのローカル変数宣言ですが、Aはローカル変数宣言または型コンテキストには適用できません。

  • レコード・コンポーネントですが、Aは、レコード・コンポーネント宣言、フィールド宣言、メソッド宣言、仮パラメータ宣言および例外パラメータ宣言、または型コンテキストには適用できません。

これらの11個の句のうち6個は、この項で前述した6つの構文の場所を特徴付けるため、「...または型コンテキスト内」に言及しています。注釈は宣言または型に妥当に適用できます。 さらに、クラスおよびインタフェース宣言と型パラメータ宣言の2つの11個の句のうち、クラス、インタフェースまたは型パラメータの宣言にインタフェースがメタ注釈付き(したがって、型コンテキストに適用可能)の注釈を適用できると便利な場合があるため、「...」または型コンテキストに言及します。

次の両方に当てはまる場合、型注釈はadmissibleです。

  • 注釈がもっとも近い単純名は、PackageNameではなくTypeNameとして分類されます。

  • 注釈がもっとも近い単純名の後に「.」と別のTypeName (つまり、注釈が@Foo T.Uとして表示される)が続く場合、UTの内部クラスを示します。

2番目の句の背後にある直観は、Outer.thisOuterで囲まれたネストされたクラスで正当な場合、実行時に一部のオブジェクトのタイプを表すため、Outerに注釈を付けることができることです。 一方、Outer.thisが正当でない場合- 出現するクラスには実行時にOuterの囲みインスタンスがないため- Outerは、完全修飾型名のパッケージ名のコンポーネントと同様に、論理的に単なる名前であるため、注釈付けされない可能性があります。

たとえば、次のプログラムでは、Bの本体にA.thisを記述することはできません。これは、Bに字句的に包含するインスタンスがないためです。 したがって、Aは型ではなく論理的に単なる名前であるため、A.B型のA@Fooを適用することはできません。


@Target(ElementType.TYPE_USE)
@interface Foo {}

class A {
    static class B {}
}

@Foo A.B x;  // Illegal

一方、次のプログラムでは、C.thisDの本体に書き込むことができます。 したがって、Cは実行時に一部のオブジェクトの型を表すため、C.D型のC@Fooを適用できます。


@Target(ElementType.TYPE_USE)
@interface Foo {}

class Test {
    static class C {
        class D {}
    }

    @Foo C.D x;  // Legal
}

インタフェースAの注釈が型コンテキスト内の型の最も外側のレベルに適用され、Aが型コンテキストまたは同じ構文の場所を占める宣言コンテキスト(存在する場合)に適用されない場合は、コンパイル時にエラーが発生します。

インタフェース Aの注釈が、型コンテキスト内の型の一部(つまり、もっとも外側のレベルではない)に適用され、型コンテキストで Aが適用されない場合は、コンパイル時にエラーが発生します。

インタフェースAの注釈が型コンテキスト内の型(または型の任意の部分)に適用され、型コンテキストでAが適用可能であるが、注釈が許可されていない場合、コンパイル時にエラーが発生します。

たとえば、注釈インタフェースTAが、@Target(ElementType.TYPE_USE)のみでメタ注釈付けされているとします。 @TA java.lang.Objectおよびjava.@TA lang.Objectという用語は、@TAがもっとも近い単純名がパッケージ名として分類されるため、無効です。 一方、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;などの宣言コンテキストと型コンテキストの両方の場所で有効です。 ここでは、@TAfの宣言(java.lang.Object型ではなく)に適用されるとみなされます。これは、TAがフィールド宣言コンテキストに適用可能であるためです。

9.7.5. 同じインタフェースの複数の注釈

Aが繰返し可能(§9.6.3)であり、AAの包含アノテーション・インタフェースの両方が宣言コンテキストまたは型コンテキスト(§9.6.4.1)に適用可能でないかぎり、同じインタフェースAの複数のアノテーションが宣言コンテキストまたは型コンテキスト(§9.6.3)に存在する場合、コンパイル時にエラーが発生します。

同じインターフェイスの複数の注釈が連続して表示されるのは慣例ですが、必須ではありません。

宣言コンテキストまたは型コンテキストに繰返し可能な注釈インタフェースAの複数の注釈がある場合、そのコンテキストにインタフェースAの明示的に宣言された注釈と、Aの包含注釈インタフェースの暗黙的に宣言された注釈が1つないかのようになります。

暗黙的に宣言された注釈はコンテナ注釈と呼ばれ、コンテキストに出現したインタフェースAの複数の注釈はベース注釈と呼ばれます。 コンテナ注釈の(配列型)value要素の要素はすべて、コンテキストに表示された左から右の順番のベース注釈です。

宣言コンテキストまたは型コンテキストで、繰返し可能な注釈インタフェースAの複数の注釈と、Aの包含する注釈インタフェースの注釈がある場合、コンパイル時にエラーが発生します。

つまり、コンテナと同じインタフェースのアノテーションも表示されるアノテーションを繰り返すことはできません。 これにより、次のような洗練されないコードは禁止されています。


@Foo(0) @Foo(1) @FooContainer({@Foo(2)})
class A {}

このコードが正当な場合、複数のレベルの包含が必要になります。最初に、インタフェースFooの基本注釈がインタフェースFooContainerの暗黙的に宣言されたコンテナ注釈によって含まれるため、その注釈とインタフェースFooContainerの明示的に宣言された注釈は、さらに別の暗黙的に宣言された注釈に含まれます。 Javaプログラミング言語の設計者は、この複雑さは望ましくないと判断しています。 インタフェースFooの基本アノテーションを、明示的な@FooContainerアノテーションで@Foo(2)と並んで発生したかのように処理するもう1つの方法は、リフレクティブ・プログラムが@FooContainerアノテーションを解釈する方法を変更する可能性があるため、望ましくありません。

宣言コンテキストまたは型コンテキストで、繰返し可能な注釈インタフェースAの注釈が1つあり、Aの包含注釈インタフェースの注釈が複数ある場合は、コンパイル時にエラーが発生します。

このルールは、次のコードを許可するために設計されています。


@Foo(1) @FooContainer({@Foo(2)})
class A {}

繰返し可能な注釈インタフェースFooのベース注釈が1つのみの場合、FooContainerFooの包含注釈インタフェースであっても、コンテナ注釈は暗黙的に宣言されません。 ただし、次のように、インタフェースFooContainerの注釈を繰り返します。


@Foo(1) @FooContainer({@Foo(2)}) @FooContainer({@Foo(3)})
class A {}

FooContainerがそれ自身の包含アノテーション・インタフェースで繰返し可能である場合でも、禁止されています。 基礎となる反復可能なインタフェースのアノテーションが存在する場合は、それ自体がコンテナであるアノテーションを繰り返すのは難しくなります。

9.8.  関数型インタフェース

ファンクション・インタフェースは、sealedが宣言されておらず、(Objectのメソッドとは別に) 1つのabstractメソッドのみを持つインタフェースであるため、単一のファンクション契約を表します。 この単一のメソッドは、スーパーインタフェースから継承されたオーバーライド等価のシグネチャを持つ複数のabstractメソッドの形式をとることができます。この場合、継承されたメソッドは論理的に単一のメソッドを表します。

sealedが宣言されていないインタフェースIの場合、Mを、クラスObjectpublicインスタンス・メソッド(§4.3.2)と同じシグネチャを持たないIのメンバーであるabstractメソッドのセットにしてください。 次に、Iファンクション・インタフェースになるのは、次の両方に当てはまるメソッドmMに存在する場合です。

  • mのシグネチャは、Mのすべてのメソッドのシグネチャのサブシグネチャ(§8.4.2)です。

  • mは、Mのすべてのメソッドの戻り型置換可能(§8.4.5)です。

クラス(§15.9)を宣言およびインスタンス化してインタフェース・インスタンスを作成する通常のプロセスに加えて、関数インタフェースのインスタンスは、メソッド参照式およびラムダ式(§15.13§15.27)を使用して作成できます。

ファンクション・インタフェースの定義では、Objectpublicメソッドでもあるインタフェースのメソッドは除外されます。 これは、複数のabstractメソッドを宣言するjava.util.Comparator<T>のようなインタフェースを機能的に処理できるようにするためで、そのうちの1つのみが実際には新しいint compare(T,T)です。 もう1つのboolean equals(Object)は、インタフェース(§9.2)で暗黙的に宣言され、インタフェースをimplementsするすべてのクラスによって自動的に実装されるabstractメソッドの明示的な宣言です。

Objectpublic以外のメソッド(clone()など)がpublicとしてインタフェースで明示的に宣言されている場合、これらのメソッドは、インタフェースをimplementsするすべてのクラスによって自動的に実装されません Objectから継承される実装はprotectedですが、インタフェース・メソッドはpublicであるため、インタフェースを実装する唯一の方法は、クラスがpublic以外のObjectメソッドをpublicメソッドでオーバーライドすることです。

例9.8-1.  関数型インタフェース

関数型インタフェースの簡単な例を次に示します:

interface Runnable {
    void run();
}

次のインタフェースは、まだObjectのメンバーではないものを宣言していないため、機能しません。

interface NonFunc {
    boolean equals(Object obj);
}

ただし、そのサブインタフェースは、Objectのメンバーではないabstractメソッドを宣言することで機能できます。

interface Func extends NonFunc {
    int compare(String o1, String o2);
}

同様に、既知のインタフェースjava.util.Comparator<T>は、abstract以外のObjectメソッドが1つあるため、機能します。

interface Comparator<T> {
    boolean equals(Object obj);
    int compare(T o1, T o2);
}

次のインタフェースは、Objectのメンバーではない1つのabstractメソッドのみを宣言しますが、Objectpublicメンバーではない2個のabstractメソッドを宣言するため、機能しません。

interface Foo {
    int m();
    Object clone();
}

例9.8-2.  関数型インタフェースおよびイレイジャ

次のインタフェース階層では、Zはファンクション・インタフェースです。これは、Objectのメンバーではない2つのabstractメソッドを継承している間、同じシグネチャを持つため、継承されたメソッドは論理的に単一のメソッドを表します。

interface X { int m(Iterable<String> arg); }
interface Y { int m(Iterable<String> arg); }
interface Z extends X, Y {}

同様に、Zは、Y.mX.mのサブシグネチャであり、X.mの戻り型置換可能であるため、次のインタフェース階層の機能インタフェースです。

interface X { Iterable m(Iterable<String> arg); }
interface Y { Iterable<String> m(Iterable arg); }
interface Z extends X, Y {}

ファンクション・インタフェースの定義では、インタフェースに2つのメンバーを含めることはできず、これらは相互にサブシグネチャではありませんが、同じ消去(§9.4.1.2)を持ちます。 したがって、Zによってコンパイル時エラーが発生する次の3つのインタフェース階層では、Zは機能インタフェースではありません(abstractメンバーはいずれも他のすべてのabstractメンバーのサブシグネチャではないため)。

interface X { int m(Iterable<String> arg); }
interface Y { int m(Iterable<Integer> arg); }
interface Z extends X, Y {}

interface X { int m(Iterable<String> arg, Class c); }
interface Y { int m(Iterable arg, Class<?> c); }
interface Z extends X, Y {}

interface X<T> { void m(T arg); }
interface Y<T> { void m(T arg); }
interface Z<A, B> extends X<A>, Y<B> {}

同様に、"機能インタフェース"の定義では、インタフェースが他のすべての要素に対してreturn-type-substitutableである場合にのみ、オーバーライド同等のシグネチャを持つメソッドを持つことができるという事実が考慮されます。 したがって、Zによってコンパイル時エラーが発生する次のインタフェース階層では、Zは関数型インタフェースではありません(そのabstractメンバーは、他のすべてのabstractメンバーに対してreturn-type-substitutableであるため)。

interface X { long m(); }
interface Y { int  m(); }
interface Z extends X, Y {}

次の例では、Foo<T,N>Barの宣言が有効です。それぞれで、mというメソッドは相互のサブシグネチャではなく、異なる消去を持ちます。 それでも、各メソッドがサブシグネチャではないという事実は、Foo<T,N>およびBarがファンクション・インタフェースではないことを意味します。 ただし、Bazはファンクション・インタフェースです。これは、Foo<Integer,Integer>から継承するメソッドが同じシグネチャを持ち、論理的に単一のメソッドを表すためです。

interface Foo<T, N extends Number> {
    void m(T arg);
    void m(N arg);
}
interface Bar extends Foo<String, Integer> {}
interface Baz extends Foo<Integer, Integer> {}

最後に、次の例では前述と同じルールを示しますが、汎用メソッドを使用しています:

interface Exec { <T> T execute(Action<T> a); }
  // Functional

interface X { <T> T execute(Action<T> a); }
interface Y { <S> S execute(Action<S> a); }
interface Exec extends X, Y {}
  // Functional: signatures are logically "the same"

interface X { <T>   T execute(Action<T> a); }
interface Y { <S,T> S execute(Action<S> a); }
interface Exec extends X, Y {}
  // Error: different signatures, same erasure

例9.8-3.  汎用関数型インタフェース

関数型インタフェースは、java.util.function.Predicate<T>などの汎用型にできます。 このようなファンクション・インタフェースは、個別のabstractメソッドを生成する方法(つまり、1つの宣言で法的にオーバーライドできない複数のメソッド)でパラメータ化できます。 たとえば:

interface I    { Object m(Class c); }
interface J<S> { S m(Class<?> c); }
interface K<T> { T m(Class<?> c); }
interface Functional<S,T> extends I, J<S>, K<T> {}

Functional<S,T>はファンクション・インタフェースです。I.mは、J.mおよびK.mの戻り型置換可能ですが、ファンクション・インタフェース・タイプFunctional<String,Integer>は、1つのメソッドでは明確に実装できません。 ただし、ファンクション・インタフェース・タイプであるFunctional<S,T>の他のパラメータ化も可能です。


関数型インタフェースの宣言により、関数型をプログラムで使用できます。 機能インタフェースには、次の4タイプがあります:

  • 非汎用(§6.1)機能インタフェースのタイプ

  • 汎用関数インタフェースのパラメータ化(§4.5)であるパラメータ化された型

  • 汎用関数インタフェースのRAW型(§4.8)

  • 概念関数インタフェースを誘導する交差型(§4.9)

特殊な状況では、交差タイプを機能インタフェース・タイプとして扱うと便利です。 通常、これは、1つ以上のマーカー・インタフェース・タイプ(Runnable & java.io.Serializableなど)と機能インタフェース・タイプの交差のようになります。 このような交差は、ラムダ式を特定の型に強制的に準拠させるキャスト(§15.16)で使用できます。 交差のインタフェース・タイプの1つがjava.io.Serializableの場合、シリアライズの特別な実行時サポートがトリガーされます(§15.27.4)。

9.9.  関数タイプ

関数型インタフェースI関数型は、Iabstractメソッドをオーバーライド(§8.4.8)するために使用できるメソッド型(§8.2)です。

Mを、Iに定義されたabstractメソッドのセットにします。 Iの関数型は、次のもので構成されます。

  • 型パラメータ、仮パラメータ型および戻り型:

    mMのメソッドにします。

    1. Mのすべてのメソッドのシグネチャのサブシグネチャであるシグネチャ。

    2. 戻り型R (voidなど)で、RMのすべてのメソッドの戻り型と同じか、Rは参照型であり、Mのすべてのメソッドの戻り型のサブタイプです(2つのメソッドが同じシグネチャを持つ場合は、任意の型パラメータ(§8.4.4)に適応した後)。

    そのようなメソッドが存在しない場合は、mM内の次のメソッドにします。

    1. Mのすべてのメソッドのシグネチャのサブシグネチャであるシグネチャ。

    2. Mのすべてのメソッドでmが戻り型置換可能(§8.4.5)になるような戻り型。

    ファンクション・タイプの型パラメータ、仮パラメータ型および戻り型は、mで指定されます。

  • throws句:

    関数型のthrows句は、次のようにMのメソッドのthrows句から導出されます。

    1. 関数型が汎用の場合、throws句は、最初に関数型の型パラメータ(§8.4.4)に適応します。

      関数型が汎用型ではなく、Mの少なくとも1つのメソッドが汎用型の場合、throws句は最初に消去されます。

    2. 次に、ファンクション・タイプのthrows句には、次の制約を満たすすべての型Eが含まれます。

      • Eは、throws句のいずれかで説明されています。

      • throws句ごとに、Eは、その句で指定されたタイプのサブタイプです。

Mの一部の戻り型がRAWで、それ以外はRAWでない場合、関数型の定義では、可能なかぎり最も具体的な型の選択が試行されます。 たとえば、戻り型がLinkedListおよびLinkedList<String>の場合、後者はすぐにファンクション型の戻り型として選択されます。 最も具体的な型がない場合、定義は最も置換可能な戻り型を見つけることによって補正されます。 たとえば、3番目の戻り型List<?>がある場合、戻り型の1つが互いのサブタイプであるとはかぎりません(RAW LinkedListList<?>のサブタイプではないため)。かわりに、LinkedList<String>がファンクション型の戻り型として選択されます。これは、LinkedListList<?>の両方で戻り型置換可能であるためです。

関数型のスローされた例外型の定義を駆動する目的は、結果のthrows句を持つメソッドが関数インタフェースの各abstractメソッドをオーバーライドできるという不変性をサポートすることです。 §8.4.6では、これは、関数型がセットM内の1つのメソッドよりも"more"例外をスローできないことを意味するため、すべてのメソッドのthrows句によってカバーされる例外型をできるだけ多く探します。

関数型インタフェース型の関数型は、次のように指定されます。

  • 非汎用関数型インタフェース Iの型の関数型は、上記で定義した関数型インタフェース Iの関数型にすぎません。

  • パラメータ化されたファンクション・インタフェース・タイプI<A1のファンクション・タイプAn>A1...Anは型で、Iの対応する型パラメータはP1です。Pnは、置換[P1:=A1、 ...、 Pn:=An]を汎用関数インタフェースI<P1の関数タイプに適用することで導出されます。Pn>

  • パラメータ化されたファンクション・インタフェース・タイプI<A1のファンクション・タイプAn>。ここで、1つ以上のA1...Anはワイルドカードで、II<T1...のワイルドカード以外のパラメータ化のファンクション・タイプです。Tn> ワイルドカード以外のパラメータ化は次のように決定されます。

    1...Pnは、対応する境界 B1...を持つ Iの型パラメータです。Bn すべての i (1 i n)について、TiAiの形式に従って導出されます。

    • Aiが型の場合、Ti = Ai

    • Aiがワイルドカードで、対応する型パラメータの境界である Biは、P1のいずれかを示します。Pnの場合、Tiは未定義であり、関数型はありません。

    • そうでない場合は、次のようになります。

      • Aiがバインドされていないワイルドカード?の場合、Ti = Biになります。

      • Aiが上限ワイルドカード? extends Uiの場合、Ti = glb(UiBi)(§5.1.10)になります。

      • Aiが下限ワイルドカード? super Liの場合、Ti = Liになります。

  • 汎用ファンクション・インタフェースI<...>のRAW型のファンクション・タイプは、汎用ファンクション・インタフェースI<...>のファンクション・タイプの消去です。

  • 概念関数インタフェースを誘導する交差タイプの関数型は、概念関数インタフェースの関数型です。

例9.9-1.  関数タイプ

次のインタフェースを確認してください。

interface X { void m() throws IOException; }
interface Y { void m() throws EOFException; }
interface Z { void m() throws ClassNotFoundException; }

ファンクション・タイプ:

interface XY extends X, Y {}

が:

()->void throws EOFException

関数の型:

interface XYZ extends X, Y, Z {}

が:

()->void (throws nothing)

次のインタフェースを確認してください。

interface A {
    List<String> foo(List<String> arg)
      throws IOException, SQLTransientException;
}
interface B {
    List foo(List<String> arg)
      throws EOFException, SQLException, TimeoutException;
}
interface C {
    List foo(List arg) throws Exception;
}

ファンクション・タイプ:

interface D extends A, B {}

が:

(List<String>)->List<String>
  throws EOFException, SQLTransientException

関数の型:

interface E extends A, B, C {}

が:

(List)->List throws EOFException, SQLTransientException

関数インタフェースのファンクション・タイプは、非決定的に定義されます。Mのシグネチャは同じですが、構文的に異なる場合があります(たとえば、HashMap.EntryMap.Entry)。戻り型は、他のすべての戻り型のサブタイプである場合がありますが、サブタイプ(たとえば、List<?>List<? extends Object>)である他の戻り型があり、スローされた型の順序は指定されません。 これらの区別は微妙ですが、時には重要になることがあります。 ただし、Javaプログラミング言語では、非決定性が重要になるような関数型は使用されません。 複数のabstractメソッド(§15.12.2.5)がある場合、最も特定のメソッドの戻り型およびthrows句も非決定的に定義されることに注意してください。

汎用関数インタフェースがワイルドカードによってパラメータ化される場合、ワイルドカードを満たし、異なる関数型を生成できる様々なインスタンス化があります。 たとえば、Predicate<Integer> (ファンクション・タイプInteger -> boolean)、Predicate<Number> (ファンクション・タイプNumber -> boolean)およびPredicate<Object> (ファンクション・タイプObject -> boolean)はそれぞれPredicate<? super Integer>です。 場合によっては、関数型が意図されているラムダ式のパラメータ型など、コンテキストから知ることができます(§15.27.3)。 その他の場合は、1つを選択する必要があります。このような状況では、境界が使用されます。 (この単純な方法では、結果の型が特定の複雑な境界を満たすことを保証できないため、すべての複雑なケースがサポートされるわけではありません。)

例9.9-2. 汎用関数タイプ

関数インタフェースのabstractメソッドは汎用であるため、関数型は汎用です。 たとえば、次のインタフェース階層の場合:

interface G1 {
    <E extends Exception> Object m() throws E;
}
interface G2 {
    <F extends Exception> String m() throws Exception;
}
interface G extends G1, G2 {}

Gのファンクション・タイプは次のとおりです。

<F extends Exception> ()->String throws F

関数型インタフェースの汎用関数型は、メソッド参照式(§15.13)で実装できますが、汎用ラムダ式の構文がないため、ラムダ式(§15.27)では実装できません。