sealedクラス

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

このドキュメントでは、Java SE 16のプレビュー機能であるsealedクラスおよびインタフェースをサポートするための、3つの仕様変更ドキュメント(i) コンテキスト・キーワード、(ii) クラスおよびインタフェースに関する一貫した用語および(iii) ローカルおよびネストされた静的宣言の修正に伴う、Java言語仕様の変更について説明します。この機能の概要は、JEP 397を参照してください。

変更はJava SE 15でのsealedクラスの最初のプレビューに対するものと同じですが、軽微な編集上の変更と次の変更が加えられています。

コンパニオン仕様変更ドキュメントでは、sealedクラスおよびインタフェースをサポートするためのJava仮想マシン仕様に必要な変更について説明します。

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

第3章: 字句構造

3.9 キーワード

ASCII文字で構成された51個の文字シーケンスは、キーワードとして使用するために予約されています。同じくASCII文字で構成された別の12 15個の文字シーケンスは、出現するコンテキストによっては、キーワードとして解釈される可能性があります

キーワードは識別子(3.8)として使用できません。

キーワード:
ReservedKeyword
ContextualKeyword
ReservedKeyword:
(次のうちの1つ)
abstract continue for new switch
assert default if package synchronized
boolean do goto private this
break double implements protected throw
byte else import public throws
case enum instanceof return transient
catch extends int short try
char final interface static void
class finally long strictfp volatile
const float native super while
_ (アンダースコア)
ContextualKeyword:
(次のうちの1つ)
exports opens to var
module provides transitive with
open requires uses yield
sealed non-sealed permits

キーワードconstおよびgotoは、現在使用されていませんが、予約されています。これにより、これらのC++のキーワードがプログラムで誤って出現した場合にJavaコンパイラがより適切なエラー・メッセージを作成できるようになります。キーワード_ (アンダースコア)は、将来パラメータ宣言で使用できるように予約されています。

コンテキスト・キーワードと一致する文字シーケンスは、シーケンスの一部が直前または直後の文字と組み合せて異なるトークンを形成できる場合は、キーワードとして扱われません。

したがって、文字シーケンスopenmoduleは、ModuleDeclarationの最初にあっても、2つのコンテキスト・キーワードではなく、単一の識別子として解釈されます。2つのキーワードを意図する場合は、空白またはコメントで区切る必要があります。

コンテキスト・キーワードと一致する他の文字シーケンスは、構文文法で次のコンテキストのいずれかに出現した場合にのみ、キーワードとして扱われます。

これらのルールは構文文法の詳細に依存しますが、Javaプログラミング言語のコンパイラは入力プログラムを完全に解析しなくてもそれらを実装できます。たとえば、コンテキスト・キーワードの有効な使用がキーワードとしてトークン化され、識別子の有効な使用が識別子としてトークン化されることがヒューリスティックで保証されているかぎり、ヒューリスティックを使用してトークナイザのコンテキスト状態を追跡できます。または、コンパイラによって常にコンテキスト・キーワードを識別子としてトークン化でき、これらの識別子の特別な使用を認識するパーサーに委ねます。

場合によっては、様々な文字列が誤ってキーワードであるとみなされます。

第5章: 変換およびコンテキスト

5.1 変換の種類

5.1.6.1 許容される絞り込み参照変換

次のことがすべて当てはまる場合、参照型Sから参照型Tへの絞り込み参照変換が存在します。

1.SおよびTはクラス型であり、|S| <: |T|または|T| <: |S|です。

2.SおよびTはインタフェース型です。

3.Sはクラス型、Tはインタフェース型であり、Sfinalクラス(8.1.1)に名前を付けません。

4.Sはクラス型、Tはインタフェース型であり、STによって名前を付けられるインタフェースを実装するfinalクラスに名前を付けます。

5.Sはインタフェース型、Tはクラス型であり、Tfinalクラスに名前を付けません。

6.Sはインタフェース型、Tはクラス型であり、TSによって名前を付けられるインタフェースを実装するfinalクラスに名前を付けます。

  1. STはどちらもクラスまたはインタフェース型であり、STによって名前を付けられるクラスまたはインタフェースと非結合ではないクラスまたはインタフェースに名前を付けます。

    別のクラスまたはインタフェースと非結合のクラスまたはインタフェースの定義は後で示します。

7. 2.Sはクラス型Objectまたはインタフェース型java.io.SerializableまたはCloneable (配列によって実装される唯一のインタフェース(10.8))であり、Tは配列型です。

8. 3.Sは配列型SC[] (型SCのコンポーネントの配列)です。Tは配列型TC[] (型TCのコンポーネントの配列)です。SCからTCへの絞り込み参照変換が存在します。

9. 4.Sは型変数であり、Sの上限からTへの絞り込み参照変換が存在します。

10. 5.Tは型変数であり、SからTの上限への拡張参照変換または絞り込み参照変換が存在します。

11. 6.Sは交差型S1 & ... & Snであり、すべてのi (1 i n)について、SiからTへの拡張参照変換または絞り込み参照変換が存在します。

12. 7.Tは交差型T1 & ... & Tnであり、すべてのi (1 i n)について、SからTiへの拡張参照変換または絞り込み参照変換が存在します。

クラスまたはインタフェースは、(null値以外に)共通のインスタンスを持たないことを静的に決定できる場合は、別のクラスまたはインタフェースと非結合であると言い表されます。次のように定義されます:

  1. C <: Iではなく、次のケースのいずれかが該当する場合、Cという名前のクラスはIという名前のインタフェースと非結合です。

    1. Cfinalです。

    2. Csealedであり、Cの許容された直接サブクラスすべてがIと非結合です。

    3. Cは自由に拡張可能であり(8.1.1.2)、Isealedであり、CIの許容された直接サブクラスおよびサブインタフェースすべてと非結合です。

    ルール(b)および(c)では、sealed階層は単純に解凍されます。重要なケースはルール(a)です。次の宣言を考えてみましょう。

    クラスCfinalであり、Iを実装しないため、IのインスタンスでもあるCのインスタンスは存在せず、これらは非結合です。したがって、CからIへの絞り込み参照変換はありません。

    対照的に、次の宣言を考えてみましょう。

    クラスDJを実装しませんが、それでもDのインスタンスがJのインスタンスである可能性があります。たとえば、(場合によっては後で)次の宣言が行われている場合です。

    このため、DJと非結合ではなく、DからJへの絞り込み参照変換があります。

  2. CIと非結合の場合、Iという名前のインタフェースはCという名前のクラスと非結合です。

  3. C <: Dではなく、D <: Cではない場合、Cという名前のクラスはDという名前の別のクラスと非結合です。

  4. I <: Jではなく、J <: Iではなく、さらに次のケースのいずれかが該当する場合、Iという名前のインタフェースはJという名前の別のインタフェースと非結合です。

    1. Isealedであり、Iの許容された直接サブクラスおよびサブインタフェースすべてがJと非結合です。

    2. Jsealedであり、IJの許容された直接サブクラスおよびサブインタフェースすべてと非結合です。

    このルールにより、2つの拡張インタフェース(9.1.1.4)が非結合であることはありません。

第6章: 名前

6.1 宣言

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

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

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

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

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

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

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

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

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

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

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

  5. sealedクラスまたはインタフェース宣言のpermits句内(8.1.69.1.4)。

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

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

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

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

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

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

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

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

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

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

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

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

...

6.5 名前の意味の確認

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

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

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

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

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

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

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

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

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

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

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

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

第8章: クラス

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

最上位クラスは、ネストしたクラスではないクラスです。

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

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

名前付きクラスはabstract (8.1.1.1)として宣言でき、これが不完全に実装されている場合は抽象であると宣言する必要があります。このようなクラスはインスタンス化できませんが、サブクラスによって拡張できます。

クラスを拡張できる度合いは、明示的に制御できます(8.1.1.2)。クラスはsealedとして宣言される場合があります。この場合、sealedクラスを直接拡張するクラスの固定セットが存在します。クラスはfinalとして宣言できます。この場合、anyサブクラスを持つことはできません。

クラスがpublicとして宣言されている場合、そのモジュールの任意のパッケージ内のコードから、および場合によっては他のモジュール内のコードから参照できます。Objectを除く各クラスは、既存の単一のクラスの拡張(つまり、サブクラス) (8.1.4)であり、インタフェースを実装する場合があります(8.1.5)。クラスは汎用(8.1.2)である場合があります。つまり、クラスは、クラスの様々なインスタンス間でバインディングが異なる可能性のある型変数を宣言できます。

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

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

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

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

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

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

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

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

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

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

8.1 クラス宣言

クラス宣言では、新しいクラスを指定します。

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

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

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

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

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

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

8.1.1 クラス修飾子

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

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

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

アクセス修飾子public (6.6)は、最上位クラス(7.6)およびメンバー・クラス(8.59.5)のみに関連します。

修飾子protectedprivateおよびstaticは、メンバー・クラスのみに関連します。

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

クラス宣言に複数のクラス修飾子sealednon-sealedおよびfinalが含まれる場合、コンパイル時にエラーになります。

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

8.1.1.2 sealedクラスおよびfinalクラス

クラス階層の拡張性は、オブジェクト指向のプログラミングの重要な機能です。ただし、この拡張性の明示的な制御が望ましい状況があります。特に、拡張性がコードの継承および再利用のメカニズムとしてではなく、ドメイン内の値の種類をリストする方法として使用されている場合です。

直接サブクラスの空でない固定セットが望ましい場合、クラスをsealedと宣言できます。sealedクラスは、直接サブクラスの特定セットを許可します(8.1.6)。

クラスはその定義が完全であり、サブクラスを目的としない、または必要としない場合にfinalと宣言されます。

finalクラスの名前が別のクラス宣言のextends句(8.1.4)内に出現する場合、コンパイル時にエラーになります。これは、finalクラスがサブクラスを持てないことを意味します。

このルールはすでに8.1.4に記載されています。

クラスがfinalおよびabstractを宣言する場合はコンパイル時にエラーが発生します。これは、このようなクラスの実装が完了しないことを示しています(8.1.1.1)。

finalクラスはサブクラスを持つことができないため、finalクラスのメソッドがオーバーライドされることはありません(8.4.8.1)。

sealedでもfinalでもないクラスは、自由に拡張可能です。ほとんどのクラスは、デフォルトで自由に拡張可能です。sealedクラスを直接拡張する(8.1.4)かsealedインタフェースを直接実装する(8.1.5)クラスは、non-sealedと宣言されている場合にのみ自由に拡張可能です。

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

enum型は、(暗黙的に) finalまたはsealed (8.9)であるため、sealedインタフェースを実装できます。

JEP 395は、Javaプログラミング言語でレコード・クラスをサポートすることを提案しています。レコード・クラスは暗黙的にfinalであるため、sealedインタフェースを実装できます。

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

sealedキーワードには、直接サブクラスがfinalsealedまたはnon-sealedのいずれかであるかどうかとは関係なく、これらすべてを明示的に宣言するよう強制できるという効果があります。これにより、sealedクラス階層を誤って不要なサブクラス化に公開することが回避されます。

8.1.4 スーパークラス

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

ClassExtends:
extends ClassType

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

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

sealed (8.1.1.2)であるクラスにClassTypeが名前を付け、現在のクラスがそのsealedクラスの許容された直接サブクラス(8.1.6)ではない場合は、コンパイル時にエラーが発生します。

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

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

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

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

クラスの直接スーパークラスは、その直接スーパークラスの型によって名前を付けられるクラスです。直接スーパークラスは、その実装が現在のクラスの実装の導出元であるクラスです。

スーパークラス関係は、直接スーパークラス関係の推移閉包です。次のいずれかを満たしている場合、クラスAはクラスCのスーパークラスです。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

ClassImplements:
implements InterfaceTypeList
InterfaceTypeList:
InterfaceType {, InterfaceType}

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

sealed (9.1.1.4)であるインタフェースにInterfaceTypeが名前を付け、宣言されるクラスが名前を付けられるインタフェースの許容された直接サブクラス(9.1.4)ではない場合、コンパイル時にエラーが発生します。

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

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

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

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

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

宣言にimplements句がないクラスには、直接スーパーインタフェース型はありません。例外として、15.9.5で定義されているように、匿名クラスにはスーパーインタフェース型がある場合があります。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

...

8.1.6 許容された直接サブクラス

これは新しいサブセクションです既存のサブセクション8.1.6「クラス本体およびメンバー宣言」は8.1.7に番号が変更されており、それを参照している既存のすべての箇所は番号を変更する必要があります。

クラス宣言内のオプションのpermits句は、sealedクラス(8.1.1.2)を直接拡張することが許可されるクラスを指定します。

ClassPermits
permits TypeName {, TypeName }

クラス宣言にpermits句が含まれるが、宣言されたクラスがsealedではない場合、コンパイル時にエラーになります。

クラス宣言内のpermits句のTypeNameはすべて、アクセス可能なクラス(6.6)を表す必要があります。そうでない場合、コンパイル時にエラーが発生します。

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

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

package p; sealed class A permits B, C, p.B {} // Error! non-sealed class B extends A {}
non-sealed class C extends A {}

クラスCの宣言によってpermits句内のクラスに名前が付けられるが、Cが名前付きクラスの直接スーパークラス(8.1.4)ではない場合、コンパイル時にエラーになります。

sealedクラスCが名前が付けられたモジュールに属する場合、Cの宣言のpermitsで名前が付けられたすべてのクラスはCと同じモジュールに属する必要があります。そうでない場合、コンパイル時にエラーが発生します。

sealedクラスCが名前が付けられていないモジュールに属する場合、Cの宣言のpermitsで名前が付けられたすべてのクラスはCと同じパッケージに属する必要があります。そうでない場合、コンパイル時にエラーが発生します。

sealedクラス階層は、様々なメンテナンス・ドメインにわたって宣言されることを意図していません。モジュールは循環方式で相互に依存することはできませんが、sealedクラスおよびその直接サブクラスは循環方式で相互に依存する必要があります(permitsおよびextends句のそれぞれ)。したがって、必然的に、sealedクラスとその直接サブクラスが同じモジュール内で共存する必要があります。名前が付けられていないモジュールでは、sealedクラスとその直接サブクラスが同じパッケージに属する必要があります。

sealedクラスC許容された直接サブクラスは、そのpermits句によってリストされたクラスです。または、Cpermits句がない場合は、Cと同じコンパイル・ユニット(7.3)で宣言された、正規名(6.7)を持ち、直接スーパークラスがCである各クラスです。

つまり、sealedクラスCpermits句がない場合は、Cを直接スーパークラスとしてリストする、同じコンパイル・ユニット内のすべての最上位およびメンバー・クラスを含むものが推測されます。正規名を持つという要件は、ローカル・クラスまたは匿名クラスは考慮されないことを意味します。

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

8.9 enumクラス

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

EnumDeclaration:
{ClassModifier} enum TypeIdentifier [ClassImplements] EnumBody

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

enum宣言に修飾子abstractまたはfinalsealedまたはnon-sealedがある場合、コンパイル時にエラーになります。

enum宣言は、クラス本体を持つenum定数(8.9.1)が少なくとも1つ含まれないかぎり、暗黙的にfinalです。

enum宣言は、次のとおり、暗黙的にfinalであるか暗黙的にsealedです。

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

これは、内部クラスには定数変数を除き、staticを含めることができないため、enumクラスを内部クラスのメンバーとして定義できないことを示しています(8.1.3)。

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

enum宣言にはextends句はありません。enumクラスEの直接スーパークラス型はEnum<E>です(8.1.4)。

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

コンパイル時のエラーに加え、さらに3つのメカニズムにより、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に規定されています。

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

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

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

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

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

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

第9章 インタフェース

インタフェース宣言により、1つ以上のクラスによって実装できる新しいインタフェースが導入されます。プログラムでは、インタフェースを使用して、それ以外に無関係なクラスの一般的なスーパータイプを指定できます。

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

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

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

注釈インタフェース(9.6)とは、特別な構文で宣言されたインタフェースであり、注釈の反射型の表現で実装するためのものです(9.7)。

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

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

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

クラスとは異なり、インタフェースをfinalとして宣言することはできません。ただし、sealedインタフェースを直接実装または拡張できるクラスおよびインタフェースの固定セットがある場合、インタフェースをsealed (9.1.1.4)として宣言できます。

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

9.1 インタフェース宣言

インタフェース宣言はインタフェースを指定します。標準インタフェース宣言注釈インタフェース宣言(9.6)という2種類のインタフェース宣言があります。

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

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

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

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

9.1.1 インタフェース修飾子

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

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

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

修飾子protectedprivateおよびstaticは、メンバー・インタフェース(8.59.5)のみに関連します。

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

インタフェースがsealednon-sealedの両方として宣言されている場合、コンパイル時にエラーになります。

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

9.1.1.4 sealedインタフェース

直接サブクラスおよびサブインタフェースの既知の空でない固定セットがあり、それ以上の直接サブクラスまたはサブインタフェースは望ましくないか不要な場合、インタフェースをsealedと宣言できます。sealedインタフェースは、直接サブクラスおよびサブインタフェースの特定セットを許可します(9.1.4)。

sealedではないインタフェースは自由に拡張可能です。ほとんどのインタフェースは、デフォルトで自由に拡張可能です。sealedインタフェースを直接拡張する(9.1.3)インタフェースは、non-sealedと宣言されている場合にのみ自由に拡張可能です。

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

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

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

extends句が指定されている場合、宣言されるインタフェースにより、リストされている各インタフェース型が拡張され、リストされている各インタフェース型のメンバー・クラスおよびインタフェース、インスタンス・メソッド、定数が継承されます。

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

InterfaceExtends:
extends InterfaceTypeList

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

InterfaceTypeList:
InterfaceType {, InterfaceType}

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

sealed (9.1.1.4)であるインタフェースにInterfaceTypeが名前を付け、現在のインタフェースがそのsealedインタフェースの許容された直接サブインタフェース(9.1.4)ではない場合、コンパイル時にエラーが発生します。

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

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

あるインタフェースが別のインタフェースの直接スーパーインタフェース型のいずれかによって名前を付けられる場合、最初のインタフェースは2番目のインタフェースの直接スーパーインタフェースです。

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

インタフェースは、その直接スーパーインタフェースの直接サブインタフェース、およびそのスーパーインタフェースそれぞれのサブインタフェースであると言い表されます。

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

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

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

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

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

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

これは新しいサブセクションです既存のサブセクション9.1.4「インタフェース本体およびメンバー宣言」は9.1.5に番号が変更されており、それを参照している既存のすべての箇所は番号を変更する必要があります。

インタフェース宣言内のオプションのpermits句は、sealedインタフェース(9.1.1.4)を直接実装または拡張することが許可されているクラスおよびインタフェースをリストします。

InterfacePermits
permits TypeName {, TypeName }

インタフェース宣言にpermits句が含まれるが、宣言されたインタフェースがsealedではない場合、コンパイル時にエラーになります。

インタフェース宣言内のpermits句のTypeNameはすべて、アクセス可能なクラスまたはインタフェース(6.6)を表す必要があります。そうでない場合、コンパイル時にエラーが発生します。

単一のpermits句内で1つのクラスまたはインタフェースに複数回名前が付けられると、コンパイル時にエラーが発生します。このことは、クラスまたはインタフェースに別の方法で名前が付けられている場合でも当てはまります。

クラスまたはインタフェースの正規名を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 {}

インタフェースIの宣言によってpermits句内のクラスまたはインタフェースに名前が付けられるが、Iが名前付きクラスまたはインタフェースの直接スーパーインタフェース(8.1.59.1.3)ではない場合、コンパイル時にエラーになります。

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

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

sealedインタフェース階層は、様々なメンテナンス・ドメインにわたって宣言されることを意図していません。モジュールは循環方式で相互に依存することはできませんが、sealedインタフェースとその直接サブクラスおよびサブインタフェースは循環方式で相互に依存する必要があります(permitsimplementsおよびextends句のそれぞれ)。したがって、必然的に、sealedインタフェースとその直接サブクラスおよびサブインタフェースが同じモジュール内で共存する必要があります。名前が付けられていないモジュールでは、sealedインタフェースとその直接サブクラスおよびサブインタフェースが同じパッケージに属する必要があります。

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

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

9.6 注釈インタフェース

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

AnnotationDeclaration:
{InterfaceModifier} @ interface TypeIdentifier AnnotationInterfaceBody

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

注釈宣言に修飾子sealed (9.1.1.4)がある場合は、コンパイル時にエラーが発生します。

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

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

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

すべての注釈インタフェースの直接スーパーインタフェースはjava.lang.annotation.Annotationです(9.1.3)。

AnnotationInterfaceDeclaration構文のため、注釈インタフェース宣言は汎用にはできず、extends句は許可されません。

注釈インタフェースではスーパークラス型またはスーパーインタフェース型を明示的に宣言できないため、注釈インタフェースのサブインタフェース自体が注釈インタフェースになることはありません。同様に、java.lang.annotation.Annotationはそれ自体が注釈インタフェースではありません。

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

これらのメソッドは注釈インタフェースの要素を定義するものではないため、この型の注釈で使用することは不正になります(9.7)。このルールがなければ、要素が注釈で表現できる型であることや、それらのアクセサ・メソッドを使用できることを確認できません。

ここで明示的に修正していないかぎり、標準インタフェース宣言に適用されるルールはすべて、注釈宣言に適用されます。

たとえば、注釈インタフェースは標準のクラスおよびインタフェースと同じ名前空間を共有し、注釈宣言はインタフェース宣言と同じスコープおよびアクセス可能性を持ちます。

9.8 関数型インタフェース

関数型インタフェースは、(Objectのメソッドとは別に) 1つのabstractメソッドのみを持ち、そのため単一の関数規約を表す、宣言されたsealedではないインタフェースです。この「単一」のメソッドは、スーパーインタフェースから継承されたオーバーライドと同等のシグネチャを持つ複数のabstractメソッドの形式をとることができます。この場合、継承されたメソッドは論理的に単一のメソッドを表します。

sealedとして宣言されていないIの場合、Mを、クラスObject (4.3.2)の任意のpublicインスタンス・メソッドと同じシグネチャを持たないIのメンバーであるabstractメソッド・セットにします。これにより、次の両方が当てはまるメソッドmM内に存在する場合、I関数型インタフェースになります。

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

関数型インタフェースの定義では、Object内のpublicメソッドでもあるインタフェース内のメソッドは除外されます。これにより、複数のabstractメソッドを宣言するが、そのうち1つ(int compare(T,T))のみが本当に「新しい」、java.util.Comparator<T>のようなインタフェースを関数的に処理できるようになります。もう1つのboolean equals(Object)は、abstractメソッドの明示的な宣言です。このメソッドはそれ以外の場合は、インタフェース(9.2)内で暗黙的に宣言され、インタフェースをimplementsするすべてのクラスによって自動的に実装されます。

Objectの非publicメソッド(clone()など)がpublicとしてインタフェース内で明示的に宣言される場合、これらは、インタフェースをimplementsするすべてのクラスによって自動的に実装されることはありません。インタフェース・メソッドがpublicである場合、Objectから継承された実装はprotectedされるため、インタフェースを実装する唯一の方法は、クラスが非publicObjectメソッドを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のメンバーではないabstractメソッドを1つのみ宣言するとともに、Objectpublicメンバーではないabstractメソッドを2つ宣言するため、関数型ではありません。

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

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

次のインタフェース階層では、Zは関数型インタフェースです。なぜなら、これは、Objectのメンバーではないabstractメソッドを2つ継承しますが、これらは同じシグネチャを持つため、継承したメソッドが論理的に単一のメソッドを表すからです。

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つのメンバーを1つのインタフェースが持つことはできないという事実を考慮します(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> {}

同様に、「関数型インタフェース」の定義では、1つのインタフェースが持つことができるのは、1つのメソッドが他のすべてのメソッドに対して戻り型置換可能である場合にオーバーライド同等シグネチャを持つメソッドのみであるという事実が考慮されます。このため、Zがコンパイル時のエラーの原因となる次のインタフェース階層では、Zは関数型インタフェースではありません。(なぜなら、そのいずれのabstractメンバーも、他のすべてのabstractメンバーに対して戻り型置換可能ではないからです)

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メソッド(つまり、単一の宣言を使用して正式にオーバーライドできない複数のメソッド)を生成する方法でパラメータ化される場合があります。次に例を示します。

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.mJ.mおよびK.mに戻り型置換可能ですが、単一のメソッドを使用して関数型インタフェース型Functional<String,Integer>を実装できないことは明確です。ただし、関数型インタフェースであるFunctional<S,T>の他のパラメータ化は可能です。

関数型インタフェースの宣言により、関数型インタフェース型をプログラム内で使用できるようになります。関数型インタフェース型には4つの種類があります。

特別な状況下では、交差型を関数型インタフェース型として扱うと役に立ちます。通常、これは、1つ以上のマーカー・インタフェース型を持つ関数型インタフェースの交差のように見えます(Runnable & java.io.Serializableなど)。このような交差は、ラムダ式を特定の型に一致させるよう強制するキャスト(15.16)で使用できます。交差内のインタフェース型の1つがjava.io.Serializableである場合、直列化用の特別な実行時サポートがトリガーされます(15.27.4)。

第13章: バイナリ互換性

13.4 クラスの展開

13.4.2 sealednon-sealedおよびfinalクラス

finalクラスに関する説明は、新しいサブセクション13.4.2.3に移動しました。

13.4.2.1 sealedクラス

自由に拡張可能であったクラス(8.1.1.2)がsealedとして宣言されるよう変更される場合、そのpermits句に含まれていないこのクラスの既存のサブクラスのバイナリがロードされると、IncompatibleClassChangeErrorがスローされます。このような変更を広範に配布されるクラスに対して行うことはお薦めしません。

finalとして宣言されたクラスをsealedとして宣言されるよう変更しても、既存のバイナリとの互換性が失われることはありません。

sealed直接スーパークラスまたはsealed直接スーパーインタフェースを持たないクラスからsealed修飾子を削除しても、既存のバイナリとの互換性が失われることはありません。

sealedクラスCsealed直接スーパークラスまたはsealed直接スーパーインタフェースがある場合、sealed修飾子を単純に削除することは、Cがコンパイルされないことを意味します。sealed直接スーパークラスまたはsealed直接スーパーインタフェースを持つすべてのクラスは、finalsealedまたはnon-sealed (8.1.1.2)である必要があるためです。

13.4.2.2 non-sealedクラス

sealedとして宣言されたクラスをnon-sealedとして宣言されるよう変更しても、既存のバイナリとの互換性が失われることはありません。

finalとして宣言されたクラスをnon-sealedとして宣言されるよう変更しても、既存のバイナリとの互換性が失われることはありません。

non-sealedクラスCには、sealed直接スーパークラスまたはsealed直接スーパーインタフェース(8.1.1.2)が必要です。non-sealed修飾子をCから単純に削除することは、Cがコンパイルされないことを意味します。sealed直接スーパークラスまたはsealed直接スーパーインタフェースを持つすべてのクラスは、finalsealedまたはnon-sealed (8.1.1.2)である必要があるためです。

13.4.2.3 finalクラス

finalとして宣言されなかったクラスがfinalとして宣言されるよう変更される場合、finalクラスはサブクラスを持つことができないため、このクラスの既存のサブクラスのバイナリがロードされると、VerifyErrorがスローされます。このような変更を広範に配布されるクラスに対して行うことはお薦めしません。

finalとして宣言されたクラスがfinalとして宣言されないよう変更される場合、既存のバイナリとの互換性が失われることはありません。

sealed直接スーパークラスまたはsealed直接スーパーインタフェースを持たないクラスからfinal修飾子を削除しても、既存のバイナリとの互換性が失われることはありません。

finalクラスCsealed直接スーパークラスまたはsealed直接スーパーインタフェースがある場合、final修飾子を単純に削除することは、Cがコンパイルされないことを意味します。sealed直接スーパークラスまたはsealed直接スーパーインタフェースを持つすべてのクラスは、finalsealedまたはnon-sealed (8.1.1.2)である必要があるためです。

13.4.5 許容された直接サブクラス

sealedクラスの許容された直接サブクラス(8.1.6)のセットにクラスを追加しても、既存のバイナリとの互換性が失われることはありません。

sealedクラスの許容された直接サブクラス(8.1.6)のセットからクラスが削除される場合、削除されるクラスの既存のバイナリがロードされると、IncompatibleClassChangeErrorがスローされます。

13.5 インタフェースの展開

既存のセクション13.5.2は13.5.3に番号が変更されています。既存のセクション13.5.3~13.5.7は13.5.5~13.5.9に番号が変更されています。

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

自由に拡張可能であったインタフェース(9.1.1.4)がsealedとして宣言されるよう変更される場合、そのpermits句に含まれていないこのインタフェースの既存のサブクラスまたはサブインタフェースのバイナリがロードされると、IncompatibleClassChangeErrorがスローされます。このような変更を広範に配布されるクラスに対して行うことはお薦めしません。

sealed直接スーパーインタフェースを持たないインタフェースからsealed修飾子を削除しても、既存のバイナリとの互換性が失われることはありません。

sealedインタフェースIsealed直接スーパーインタフェースがある場合、sealed修飾子をIから単純に削除することは、Iがコンパイルされないことを意味します。sealed直接スーパーインタフェースを持つすべてのインタフェースは、sealedまたはnon-sealed (9.1.1.4)である必要があるためです。

sealedとして宣言されているインタフェースをnon-sealedとして宣言されるよう変更しても、既存のバイナリとの互換性が失われることはありません。

non-sealedインタフェースIには、sealed直接スーパーインタフェース(9.1.1.4)が必要です。non-sealed修飾子をIから単純に削除することは、Iがコンパイルされないことを意味します。sealed直接スーパーインタフェースを持つすべてのインタフェースは、sealedまたはnon-sealed (9.1.1.4)である必要があるためです。

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

sealedインタフェースの許容された直接サブクラスまたはサブインタフェース(9.1.4)のセットにそれぞれクラスまたはインタフェースを追加しても、既存のバイナリとの互換性が失われることはありません。

sealedインタフェースの許容された直接サブクラスまたはサブインタフェース(9.1.4)のセットからクラスまたはインタフェースが削除される場合、削除されるクラスまたはインタフェースの既存のバイナリがロードされると、IncompatibleClassChangeErrorがスローされます。

第14章: ブロックおよび文

14.3 ローカル・クラスおよびインタフェースの宣言

ローカル・クラスまたはローカル・インタフェースは、ネストしたクラスまたはインタフェース(89)であり、その宣言はブロックに直接含まれます(14.2)。

LocalClassOrInterfaceDeclaration:
ClassDeclaration
NormalInterfaceDeclaration

ローカル・クラスはenumクラス(8.9)である場合があります。ローカル・インタフェースが注釈インタフェース(9.6)であることはありません

ローカル・クラスおよびインタフェース宣言は、ブロック内の文と自由に混在できます。

ローカル・クラスまたはインタフェースは、パッケージ、クラスまたはインタフェースのメンバーではありません。無名クラス(15.9.5)とは異なり、ローカル・クラスまたはインタフェースには単純名があります(6.2, 6.7)。

ローカルenumクラスおよびローカル・インタフェースは暗黙的にstaticです(8.1.1.49.1.1.3)。暗黙的にstaticでないローカル・クラスは内部クラスです(8.1.3)。

ローカル・クラスまたはインタフェースがアクセス修飾子publicprotectedまたはprivate (6.6)のいずれか、または修飾子修飾子static (8.1.1)sealedまたはnon-sealed (8.1.1.2)のいずれかを使用して宣言されている場合、コンパイル時にエラーが発生します。

ローカル・クラスの直接スーパークラスまたは直接スーパーインタフェースがsealed (8.1.1.2)である場合、コンパイル時にエラーが発生します。

ローカル・インタフェースの直接スーパーインタフェースがsealed (9.1.1.4)である場合、コンパイル時にエラーが発生します。

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

例14.3-1.ローカル・クラスおよびインタフェースの宣言

ここでは、前述のルールのいくつかの項目の例を示します。

class Global {
    class Cyclic {}

    void foo() {
        new Cyclic(); // create a Global.Cyclic
        class Cyclic extends Cyclic {} // circular definition

        {
            class Local {}
            {
                class Local {} // compile-time error
            }
            class Local {} // compile-time error
            class AnotherLocal {
                void bar() {
                    class Local {} // ok
                }
            }
        }
        class Local {} // ok, not in scope of prior Local
    }
}

メソッドfooの最初の文は、ローカル・クラス宣言のスコープの前に出現しているため、ローカル・クラスCyclicのインスタンスではなく、メンバー・クラスGlobal.Cyclicのインスタンスを作成しています。

ローカル・クラス宣言のスコープが(本体のみではなく)宣言全体を含んでいることは、ローカル・クラスCyclicの定義がGlobal.Cyclicではなくそれ自体を拡張しているため、真に周期的であることを意味します。その結果、ローカル・クラスCyclicの宣言はコンパイル時に拒否されます。

ローカル・クラス名は同じメソッド(または場合によってはコンストラクタまたはイニシャライザ)内で再宣言することはできないため、Localの2回目と3回目の宣言の結果、コンパイル時にエラーが発生します。ただし、Localは、AnotherLocalなど、より深くネストした別のクラス内のコンテキスト内で再宣言できます。

Localの最後の宣言は、Localの前の宣言のスコープ外で行われているため正当です。

第15章: 式

15.9 クラス・インスタンス作成式

15.9.1 インスタンス化対象のクラスの確認

ClassOrInterfaceTypeToInstantiateが(<>ではなく)TypeArgumentsで終わる場合、ClassOrInterfaceTypeToInstantiateが整形式のパラメータ化された型(4.5)を表す必要があります。そうでない場合、コンパイル時にエラーが発生します。

ClassOrInterfaceTypeToInstantiate<>で終わるが、ClassOrInterfaceTypeToInstantiate内のIdentifierで示されたクラスまたはインタフェースが汎用ではない場合、コンパイル時にエラーが発生します。

クラス・インスタンス作成式がクラス本体で終わる場合、インスタンス化対象のクラスは匿名クラスです。次に、

クラス・インスタンス作成式が匿名クラスを宣言しない場合、次のようになります。

15.9.5 匿名クラス宣言

匿名クラスは、クラス・インスタンス作成式またはクラス本体で終わるenum定数(8.9.1)によって暗黙的に宣言されます。

匿名クラスは決してabstract (8.1.1.1)ではありません。

クラス・インスタンス作成式によって宣言される匿名クラスは、final (8.1.1.2)になることはありません。enum定数によって宣言される匿名クラスは、常にfinalです。

匿名クラスはsealed (8.1.1.2)になることはないため、許容された直接サブクラス(8.1.6)はありません。

finalの処理は、キャスト(特に、キャスト演算子(5.5)で許可された絞り込み参照変換)で関連します。これはサブクラス化には関連しません。これは、匿名クラスはextends句(8.1.4)によって名前を付けることができず、匿名クラスが非finalであっても、匿名クラスのサブクラスを宣言できないためです。

匿名クラスは常に内部クラス(8.1.3)です。

クラス・インスタンス作成式によって宣言される匿名クラスの直接スーパークラス型または直接スーパーインタフェース型は式によって指定され(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が明示的に注釈として付けられていない場合)、気付かれずに間違った状態になっているプログラムを回避しやすくなります。