sealedクラス

Java®言語仕様バージョン15+36-1562への変更

このドキュメントでは、Java SE 15のプレビュー機能であるsealedクラスおよびインタフェースをサポートするためのJava言語仕様に対する変更について説明します。この機能の概要は、JEP 360を参照してください。

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

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

第3章: 字句構造

3.8 識別子

識別子は、Javaの文字およびJavaの数字からなる無限長のシーケンスで、先頭はJavaの文字です。

Identifier:
KeywordBooleanLiteralNullLiteralのいずれでもないIdentifierChars
IdentifierChars:
JavaLetter {JavaLetterOrDigit}
JavaLetter:
「Javaの文字」である任意のUnicode文字
JavaLetterOrDigit:
「Javaの文字または数字」である任意のUnicode文字

「Javaの文字」は、メソッドCharacter.isJavaIdentifierStart(int)がtrueを返す文字です。

「Javaの文字または数字」は、メソッドCharacter.isJavaIdentifierPart(int)がtrueを返す文字です。

「Javaの文字」には、大文字および小文字のASCIIのラテン文字A-Z (\u0041-\u005a)、a-z (\u0061-\u007a)、および歴史的理由によってASCIIのドル記号($または\u0024)とアンダースコア(_または\u005f)が含まれます。ドル記号は、機械的に生成されたソース・コードで使用するか、まれに、レガシー・システムで既存の名前にアクセスする場合のみ使用する必要があります。アンダースコアは、2つ以上の文字で構成された識別子には使用できますが、キーワードであるために1文字の識別子として使用することはできません。

「Javaの数字」には、ASCII数字0-9 (\u0030-\u0039)が含まれます。

文字および数字は、Unicode文字セット全体から取得できます。Unicode文字セットは、世界中で現在使用されているほとんどの作成スクリプトをサポートしています。これには、中国語、日本語および韓国語の大規模なセットが含まれます。これによりプログラマは、ネイティブ言語で作成されるプログラムで識別子を使用できるようになります。

識別子には、キーワード(3.9)、ブール・リテラル(3.10.3)またはnullリテラル(3.10.7)と同じスペル(Unicode文字列)を使用することはできません。使用すると、コンパイル時にエラーが発生します。

2つの識別子が同一であるのは、無視できるモジュールを無視した後に、識別子が文字または数字ごとに同じUnicode文字を持つ場合のみです。無視できる文字は、メソッドCharacter.isIdentifierIgnorable(int)がtrueを返す文字です。外観が同じである識別子でも、依然として異なる場合があります。

たとえば、単一文字LATIN CAPITAL LETTER A (A\u0041)、LATIN SMALL LETTER A (a\u0061)、GREEK CAPITAL LETTER ALPHA (A\u0391)、CYRILLIC SMALL LETTER A (a\u0430)およびMATHEMATICAL BOLD ITALIC SMALL A (a\ud835\udc82)で構成された識別子はすべて異なります。

Unicodeの複合文字は、正規の同等の分解文字とは異なります。たとえば、LATIN CAPITAL LETTER A ACUTE (Á\u00c1)は、識別子内でLATIN CAPITAL LETTER A (A, \u0041)の直後にNON-SPACING ACUTE (´\u0301)が付いたものとは異なります。『The Unicode Standard』のセクション3.11「Normalization Forms」を参照してください。

識別子の例は、次のとおりです。

識別子varおよびyieldsealedおよびpermitsは、一部のコンテキストでは許可されていないため、制限識別子です。

タイプ識別子は、文字シーケンスvarまたは文字シーケンスyieldではない識別子です。

タイプ識別子は、permitssealedvarまたはyieldの文字シーケンスのいずれでもない識別子です。

TypeIdentifier:
permitssealedvarまたはyieldではない識別子

型識別子は、型の宣言または使用が含まれる特定のコンテキストで使用されます。たとえば、クラスの名前はTypeIdentifierである必要があるため、permitssealedvarまたはyieldという名前のクラスを宣言することは不正です(8.1)。

非修飾メソッド識別子は、文字列yieldでない識別子です。

UnqualifiedMethodIdentifier:
yield以外の識別子

この制限によって、yieldyield文(14.21)で使用でき、互換性の理由から(修飾)メソッド名としても使用できます。

3.9 キーワード

ASCII文字で構成された5152文字の列は、キーワードとして使用するために予約されており、識別子として使用することはできません(3.8)。

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

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

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

これ以外の文字列として、openmodulerequirestransitiveexportsopenstousesprovidesおよびwithの10種類の制限付きキーワードがあります。これらの文字列は、ModuleDeclarationModuleDirectiveおよびRequiresModifierプロダクションの端末として表示される場合のみ、キーワードとしてトークン化されます(7.7)。これらは、それ以外のすべての場所で識別子としてトークン化されます。これにより、制限付きキーワードの導入前に作成されたプログラムとの互換性を確保します。例外が1つあります。文字列transitiveは、ModuleDirectiveプロダクション内の文字列requiresのすぐ右側で、後ろにセパレータが続かないかぎり、キーワードとしてトークン化されます。この場合、これは識別子としてトークン化されます。

第6章: 名前

6.1 宣言

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

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

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

汎用ではない型の宣言(class C ...)では、1つのエンティティ(非汎用型(C))が宣言されます。非汎用型は、構文上の類似点を除いてRAW型ではありません。対照的に、汎用型(class C<T> ...またはinterface C<T> ...)の宣言では、汎用型(C<T>)および対応する非汎用型(C)の2つのエンティティが宣言されます。この場合、用語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.3 宣言のスコープ

...

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

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

...

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]
[Superclass] [Superinterfaces] [PermittedSubclasses]
ClassBody

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

クラス宣言内の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.5)にのみ関連し、ローカル・クラス(14.3)または匿名クラス(15.9.5)には関連しません。

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

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

クラス宣言について同じキーワードが修飾子として複数回出現する場合、またはアクセス修飾子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または直接スーパーインタフェースsealedを持ち、finalsealedまたはnon-sealedとして宣言されていない場合、コンパイル時にエラーになります。

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

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

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

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

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

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

スーパークラス:
extends ClassType

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

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

ClassTypeが、sealed (8.1.1.2)であるクラスに名前を付け、宣言対象のクラスが、名前を付けられたクラスで許可されたサブクラス(8.1.6)ではない場合、コンパイル時にエラーになります。

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

ClassTypeがクラスEnumまたはEnumの呼出しに名前を付けた場合は、コンパイル時にエラーが発生します(8.9)。

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

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

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

クラスは、直接スーパークラスの直接サブクラスであると言うことができます。直接スーパークラスは、その実装が現在のクラスの実装の導出元であるクラスです。

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

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

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

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

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

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

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

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

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

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

クラスCが参照型T依存するのは、次のいずれかの条件が満たされる場合です。

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

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

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

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

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

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

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

Superinterfaces:
implements InterfaceTypeList
InterfaceTypeList:
InterfaceType {, InterfaceType}

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

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

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

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

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

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

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

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

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

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

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

1つのクラスがそのすべてのスーパーインタフェースを実装するとされています。

1つのクラスが同時に、同じ汎用インタフェース(9.1.2)の異なるパラメータ化である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>の両方のサブタイプになろうとしているため、コンパイル時のエラーの原因となります。

宣言対象のクラスがabstractでないかぎり、各直接スーパーインタフェースのすべてのabstractメンバー・メソッドを、このクラス内の宣言によって、または直接スーパークラスまたは直接スーパーインタフェースから継承した既存のメソッド宣言によって実装する必要があります(8.4.8.1)。なぜなら、abstractではないクラスがabstractメソッド(8.1.1.1)を持つことは許可されないからです。

クラスのスーパーインタフェースのデフォルトの各メソッド(9.4.3)は、必要に応じてこのクラス内のメソッドによってオーバーライドできます。オーバーライドしない場合、デフォルトのメソッドは一般的に継承され、その動作はデフォルトの本体によって指定されるとおりです。

クラス内の単一のメソッド宣言が複数のスーパーインタフェースのメソッドを実装することは許可されています。

例8.1.5-3.スーパーインタフェースのメソッドの実装

interface Colorable {
    void setColor(int color);
    int getColor();
}
class Point { int x, y; };
class ColoredPoint extends Point implements Colorable {
    int color;
}

このプログラムは、コンパイル時のエラーの原因となります。なぜなら、ColoredPointabstractクラスではなく、インタフェースColorableのメソッドsetColorおよびgetColorの実装を提供できないからです。

次のプログラムの場合:

interface Fish  { int getNumberOfScales(); }
interface Piano { int getNumberOfScales(); }
class Tuna implements Fish, Piano {
    // You can tune a piano, but can you tuna fish?
    public int getNumberOfScales() { return 91; }
}

クラスTuna内のメソッドgetNumberOfScalesは、インタフェースFishで宣言されたメソッドと一致する名前、シグネチャおよび戻り型を持つとともに、インタフェースPiano内で宣言されたメソッドとも一致します。これは、両方を実装するとみなされます。

一方、次のような状況の場合:

interface Fish       { int    getNumberOfScales(); }
interface StringBass { double getNumberOfScales(); }
class Bass implements Fish, StringBass {
    // This declaration cannot be correct,
    // no matter what type is used.
    public ?? getNumberOfScales() { return 91; }
}

シグネチャと戻り型が、インタフェースStringBassとインタフェースStringBass内で宣言された両方のメソッドと互換性を持つ、getNumberOfScalesという名前のメソッドを宣言することは不可能です。なぜなら、1つのクラスが、同じシグネチャと異なるプリミティブ戻り型を持つ複数のメソッドを持つことはできないからです(8.4)。したがって、単一のクラスがインタフェースFishとインタフェースStringBassの両方を実装することはできません(8.4.8)。

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

これは新しいサブセクションです既存のサブセクション8.1.6「クラス本体およびメンバー宣言」は8.1.7に番号が変更されています。

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

PermittedSubclasses
permits TypeName {, TypeName }

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

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

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

クラス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である、同じコンパイル・ユニット内でC (7.3)として宣言された各最上位クラスまたはネストしたクラスです。

sealedクラスCの宣言にpermits句がなく、同じコンパイル・ユニット内でCとして宣言された最上位クラスまたはネストしたクラスによってCが直接スーパークラスとして名前が付けられていない場合、コンパイル時にエラーになります。

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

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

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

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

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

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

8.2 クラス・メンバー

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

...

8.5 メンバー型宣言

メンバー・クラスは、宣言が別のクラスまたはインタフェース宣言の本体(8.1.68.1.79.1.49.1.5)で直接囲まれたクラスです。

メンバー・インタフェースは、宣言が別のクラスまたはインタフェース宣言の本体(8.1.68.1.79.1.49.1.5)で直接囲まれたインタフェースです。

...

8.9 enum型

enum宣言は、新しいenum型(特別な種類のクラス型)を指定します。

EnumDeclaration:
{ClassModifier} enum TypeIdentifier [Superinterfaces] EnumBody

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

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

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

ネストしたenum型は暗黙的にstaticです。ネストしたenum型の宣言でstatic修飾子の重複指定が許可されています。

このことは、内部クラス(8.1.3)の本体内でenum型を宣言できないことを示します。なぜなら、内部クラスは、定数変数を除くstaticメンバーを持つことができないからです。

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

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型の外側で呼び出される可能性があるのは、囲んでいるenum型内のアクセス可能なメソッドをオーバーライド(8.4.8)する場合のみです。

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

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

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

8.9.2 enum本体宣言

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

EnumBodyDeclarations:
; {ClassBodyDeclaration}

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

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

...

第9章 インタフェース

インタフェース宣言により、メンバーがクラス、インタフェース、定数およびメソッドである新しい参照型を導入します。この型はインスタンス変数を持たず、通常、1つ以上のabstractメソッドを宣言します。それ以外の場合、無関係なクラスが、abstractメソッドの実装を提供することにより、インタフェースを実装できます。インタフェースを直接インスタンス化することはできません。

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

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

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

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

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

1つのインタフェースを1つ以上の他のインタフェースの直接拡張として宣言できます。これは、このインタフェースが、オーバーライドしたり非表示にできるメンバーを除いて、拡張するインタフェースのメンバー型、インスタンス・メソッドおよび定数をすべて継承することを意味します。

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

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

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

9.1 インタフェース宣言

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

InterfaceDeclaration:
NormalInterfaceDeclaration
AnnotationTypeDeclaration
NormalInterfaceDeclaration:
{InterfaceModifier} interface TypeIdentifier [TypeParameters]
[ExtendsInterfaces] [PermittedSubclassesAndSubinterfaces]
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に規定されています。

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

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

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

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

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

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

9.1.1.3 sealedインタフェース

インタフェースは、その直接サブクラスおよびサブインタフェースをクラスとインタフェースの固定セットに制限する方が役に立つ場合はsealedとして宣言できます。sealedインタフェースは、直接サブクラスおよびサブインタフェースの特定セットを許可します(9.1.4)。

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

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

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

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

これらの名前が付けられた他の各インタフェースは、宣言されているインタフェースの直接スーパーインタフェースです。

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

ExtendsInterfaces:
extends InterfaceTypeList

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

InterfaceTypeList:
InterfaceType {, InterfaceType}

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

InterfaceTypeが、sealed (9.1.1.3)であるインタフェースに名前を付け、宣言対象のインタフェースが、名前を付けられたインタフェースで許可されたサブインタフェース(9.1.4)ではない場合、コンパイル時にエラーになります。

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

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

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

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

KIのスーパーインタフェースである場合は常に、インタフェースIKサブインタフェースであると言われます。

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

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

次のいずれかが当てはまる場合、インタフェースIは参照型T依存します。

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

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

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

これは新しいサブセクションです既存のサブセクション9.1.4「インタフェース本体およびメンバー宣言」は9.1.5に番号が変更されています。

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

PermittedSubclassesAndSubinterfaces
permits TypeName {, TypeName }

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

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

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

インタフェース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が含まれる、同じコンパイル・ユニット内でI (7.3)として宣言された各最上位クラスまたはインタフェースまたはネストしたクラスまたはインタフェースです。

sealedインタフェースIの宣言にpermits句がなく、同じコンパイル・ユニット内でIとして宣言された最上位クラスまたはインタフェースまたはネストしたクラスまたはインタフェースによってIが直接スーパーインタフェースとして名前が付けられていない場合、コンパイル時にエラーになります。

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

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

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

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

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

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

...

9.6 注釈型

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

AnnotationTypeDeclaration:
{InterfaceModifier} @ interface TypeIdentifier AnnotationTypeBody

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

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

注釈型宣言に修飾子sealed (9.1.1.3)がある場合、コンパイル時にエラーになります。

注釈型宣言内のTypeIdentifierは、注釈型の名前を指定します。

注釈型が、囲んでいる任意のクラスまたはインタフェースと同じ単純名を持つ場合、コンパイル時にエラーになります。

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

AnnotationTypeDeclaration構文に基づいて、注釈型宣言を汎用にすることはできず、extends句は許可されません。

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

注釈型はjava.lang.annotation.Annotationから複数のメンバーを継承し、これには、Objectのインスタンス・メソッドに相当する、暗黙的に宣言されたメソッドが含まれますが、これらのメソッドは注釈型の要素を定義しません(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クラス

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

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

13.4.2.2 non-sealedクラス

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

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

13.4.2.3 finalクラス

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

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

13.4.4 スーパークラスおよびスーパーインタフェース

クラスがそれ自体のスーパークラスになると、ロード時にClassCircularityErrorがスローされます。既存のバイナリを使用して新しくコンパイルされたバイナリがロードされるときにこのような循環性の原因となるクラス階層への変更を、広範に配布されるクラスに対して行うことはお薦めしません。

クラス型のスーパークラスまたはスーパーインタフェースそれぞれの合計セットがメンバーを失わないことを前提として、クラス型の直接スーパークラスまたは直接スーパーインタフェースのセットを変更しても、既存のバイナリとの互換性が失われることはありません。

直接スーパークラスまたは直接スーパーインタフェースのセットを変更した結果、クラスまたはインタフェースがそれぞれスーパークラスまたはスーパーインタフェースではなくなったときに、変更されたクラスのバイナリとともに既存のバイナリがロードされた場合、リンク・エラーが発生する可能性があります。広範に配布されるクラスの場合、このような変更を行わないことをお薦めします。

例13.4.4-1.スーパークラスの変更

次のテスト・プログラムが存在するとします。

class Hyper { char h = 'h'; } 
class Super extends Hyper { char s = 's'; }
class Test extends Super {
    public static void printH(Hyper h) {
        System.out.println(h.h);
    }
    public static void main(String[] args) {
        printH(new Super());
    }
}

これをコンパイルして実行すると、次の出力が得られます。

h

次に、クラスSuperの新しいバージョンをコンパイルするとします。

class Super { char s = 's'; }

クラスSuperのこのバージョンはHyperのサブクラスではありません。次に、Superの新しいバージョンを使用してHyperおよびTestの既存のバイナリを実行すると、リンク時にVerifyErrorがスローされます。このようにベリファイアが異議を申し立てるのは、SuperHyperのサブクラスではないため、new Super()の結果を型Hyperの仮パラメータのかわりに引数として渡すことができないからです。

検証ステップがなければどのようになる可能性があるかについて検討することをお薦めします。この場合、プログラムを実行すると、次のように出力される可能性があります。

s

これは、ベリファイアがなければ、互換性のないバイナリ・ファイルをリンクすると、これらの各ファイルが正しいJavaコンパイラによって生成されたものであったとしても、Java型システムが無効になる可能性があることを示しています。

ここから得られる教訓は、ベリファイアがない実装やベリファイアを使用できない実装の場合、型の安全性が維持されないため、有効な実装ではないということです。

マルチcatch句(14.20)の選択肢が相互のサブクラスまたはスーパークラスであってはならないという要件は、ソースの制限にすぎません。次のクライアント・コードが正当であるとします。

try {
    throwAorB();
} catch(ExceptionA | ExceptionB e) {
    ...
}

ここで、クライアントのコンパイル時にExceptionAExceptionBがサブクラス/スーパークラス関係を持たない場合、クライアントの実行時にExceptionAExceptionBのクライアントがこのような関係を持つことに関してはバイナリ互換性があります。

このことは、クライアントに対してバイナリ互換性があるクラス変換が同じクライアントに対してソース互換性がないような他の状況に類似しています。

クラスの許可された直接サブクラス(8.1.6)またはインタフェースの許可された直接サブクラスおよびサブインタフェース(9.1.4)のセットを変更すると、変更されたクラスまたはインタフェースのバイナリとともに既存のバイナリがロードされる場合にリンク・エラーが発生する可能性があります。広範に配布されるクラスまたはインタフェースの場合、このような変更を行わないことをお薦めします。

13.5 インタフェースの展開

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

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

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

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

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

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

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

14.3 ローカル・クラス宣言

ローカル・クラスは、クラスのメンバーではなく、名前を持つネストしたクラス(8)です(6.26.7)。

ローカル・クラスはすべて内部クラスです(8.1.3)。

ローカル・クラス宣言文はすべて、ただちにブロックに含まれます(14.2)。ローカル・クラス宣言文は、ブロック内の他の種類の文と自由に混在できます。

ローカル・クラス宣言にアクセス修飾子publicprotectedまたはprivate (6.6)、または修飾子修飾子static (8.1.1)sealedまたはnon-sealed (8.1.1.2)が含まれる場合、コンパイル時にエラーが発生します。

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

ローカル・クラス宣言のスコープおよびシャドウ化については、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内の識別子によって表される型が汎用ではない場合、コンパイル時にエラーが発生します。

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

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

15.9.5 匿名クラス宣言

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

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

匿名クラスは、クラス本体を持つenum定数(8.9.1)によって宣言されないかぎり、決してfinal (8.1.1.2)ではありません。

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

匿名クラスがfinalではないという事実は、キャスト、特にキャスト演算子(5.5)で許可された絞り込み参照変換に関連しています。これはまた、サブクラス化の場合も興味深い事実です。具体的には、匿名クラスが非finalであるにもかかわらず、extends句(8.1.4)によって匿名クラスに名前を付けることができないため、匿名クラスのサブクラスを宣言することが不可能である点です。

匿名クラスは常に内部クラス(8.1.3)です。決してstatic (8.1.18.5.1)ではありません。

匿名クラスのスーパークラスまたはスーパーインタフェースは、クラス・インスタンス作成式(15.9.1)によって与えられます。この場合、コンストラクタ(15.9.3)の選択時に型引数が必要に応じて推測されます。

クラス・インスタンス作成式に<>が匿名クラスとともに使用される場合、匿名クラス本体内で宣言されたすべての非privateメソッドについて、メソッド宣言に@Override (9.6.4.4)が注釈として付けられたかのようになります。

<>が使用される場合、推測された型引数はプログラマによって予期されたとおりにはならない可能性があります。この結果、匿名クラスのスーパータイプが予期されたとおりにはならない可能性があり、匿名クラスで宣言されたメソッドが意図したとおりにスーパータイプ・メソッドをオーバーライドしない可能性があります。このようなメソッドを@Overrideが注釈として付けられたかのように扱うと(これらに@Overrideが明示的に注釈として付けられていない場合)、気付かれずに間違った状態になっているプログラムを回避しやすくなります。