このドキュメントでは、Java SE 16のプレビュー機能であるsealedクラスおよびインタフェースをサポートするための、3つの仕様変更ドキュメント(i) コンテキスト・キーワード、(ii) クラスおよびインタフェースに関する一貫した用語および(iii) ローカルおよびネストされた静的宣言の修正に伴う、Java言語仕様の変更について説明します。この機能の概要は、JEP 397を参照してください。
変更はJava SE 15でのsealedクラスの最初のプレビューに対するものと同じですが、軽微な編集上の変更と次の変更が加えられています。
特に、コンテキスト・キーワード(以前は制限付き識別子および制限付きキーワードとして記述されていました)の識別において字句文法を適用するときのコンテキストの使用方法の明確化。これについては、仕様変更ドキュメントのコンテキスト・キーワードで詳細に説明されています。キーワード
sealed、non-sealedおよびpermitsは、現在はコンテキスト・キーワードの新しいインスタンスとして定義されています(3.9)。このドキュメントは、仕様変更ドキュメントのクラスおよびインタフェースに関する一貫した用語およびローカルおよびネストされた静的宣言で詳細に説明されている変更が適用されていることを前提としています。特に、次の2つの新しい位置での静的宣言のサポートを提供します。
- ローカルで、暗黙的に静的なインタフェースおよびenumクラス
- 内部クラスの静的メンバー
これにより、ローカル・インタフェースは
sealedであってはならないという新しい要件が発生します。(14.3)sealed型階層に関してキャスト変換のチェックを強化するための絞り込み参照変換の拡張(5.1.6.1)。
sealedクラスまたはsealedインタフェースの暗黙的に宣言された許容された直接サブクラスに関する用語の改善(8.1.6、9.1.4)。
コンパニオン仕様変更ドキュメントでは、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つのキーワードを意図する場合は、空白またはコメントで区切る必要があります。
コンテキスト・キーワードと一致する他の文字シーケンスは、構文文法で次のコンテキストのいずれかに出現した場合にのみ、キーワードとして扱われます。
openおよびmoduleは、ModuleDeclaration (7.7)で終端として指定されたように出現する場合requires、exports、opens、uses、provides、toおよびwithは、ModuleDirectiveで終端として指定されたように出現する場合transitiveは、RequiresModifierで終端として指定されたように出現する場合ディレクティブ
requires transitive;はRequiresModifierを使用しないため、この場合transitiveは識別子として解釈されます。varは、LocalVariableType (14.4)またはLambdaParameterType (15.27.1)で終端として指定されたように出現する場合その他の多くのコンテキストでは、文字シーケンス
varを識別子として使用しようとすると、varは有効なTypeIdentifier (3.8)ではないため、エラーが発生します。yieldは、YieldStatement (14.21)で終端として指定されたように出現する場合その他の多くのコンテキストでは、文字シーケンス
yieldを識別子として使用しようとすると、yieldは有効なTypeIdentifierでも有効なUnqualifiedMethodIdentifierでもないため、エラーが発生します。sealed、non-sealedおよびpermitsは、NormalClassDeclaration (8.1)またはNormalInterfaceDeclaration (9.1)で非終端として指定されたように出現する場合
これらのルールは構文文法の詳細に依存しますが、Javaプログラミング言語のコンパイラは入力プログラムを完全に解析しなくてもそれらを実装できます。たとえば、コンテキスト・キーワードの有効な使用がキーワードとしてトークン化され、識別子の有効な使用が識別子としてトークン化されることがヒューリスティックで保証されているかぎり、ヒューリスティックを使用してトークナイザのコンテキスト状態を追跡できます。または、コンパイラによって常にコンテキスト・キーワードを識別子としてトークン化でき、これらの識別子の特別な使用を認識するパーサーに委ねます。
場合によっては、様々な文字列が誤ってキーワードであるとみなされます。
第5章: 変換およびコンテキスト
5.1 変換の種類
5.1.6.1 許容される絞り込み参照変換
次のことがすべて当てはまる場合、参照型Sから参照型Tへの絞り込み参照変換が存在します。
SはTのサブタイプではありません(4.10)
Tのスーパータイプであるパラメータ化型XとSのサブタイプであるパラメータ化型Yが存在し、かつXとYのイレイジャが同じである場合は、XとYは明確に異なるとは言えません(4.5)。
java.utilパッケージの型を例として使用すると、ArrayList<String>からArrayList<Object>へは絞り込み参照変換は存在せず、その逆も同様です。型引数StringとObjectは明確に異なるためです。同じ理由から、ArrayList<String>からList<Object>へは絞り込み参照変換は存在せず、その逆も同様です。明確に異なる型を拒否することは、無意味な絞り込み参照変換を防ぐ簡単な静的ゲートとなります。次のケースのいずれかが適用されます。
1.SおよびTはクラス型であり、|S| <: |T|または|T| <: |S|です。
2.SおよびTはインタフェース型です。
3.Sはクラス型、Tはインタフェース型であり、Sはfinalクラス(8.1.1)に名前を付けません。
4.Sはクラス型、Tはインタフェース型であり、SはTによって名前を付けられるインタフェースを実装するfinalクラスに名前を付けます。
5.Sはインタフェース型、Tはクラス型であり、Tはfinalクラスに名前を付けません。
6.Sはインタフェース型、Tはクラス型であり、TはSによって名前を付けられるインタフェースを実装するfinalクラスに名前を付けます。
SとTはどちらもクラスまたはインタフェース型であり、SはTによって名前を付けられるクラスまたはインタフェースと非結合ではないクラスまたはインタフェースに名前を付けます。
別のクラスまたはインタフェースと非結合のクラスまたはインタフェースの定義は後で示します。
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値以外に)共通のインスタンスを持たないことを静的に決定できる場合は、別のクラスまたはインタフェースと非結合であると言い表されます。次のように定義されます:
C
<:Iではなく、次のケースのいずれかが該当する場合、Cという名前のクラスはIという名前のインタフェースと非結合です。Cは
finalです。Cは
sealedであり、Cの許容された直接サブクラスすべてがIと非結合です。Cは自由に拡張可能であり(8.1.1.2)、Iは
sealedであり、CはIの許容された直接サブクラスおよびサブインタフェースすべてと非結合です。
ルール(b)および(c)では、
sealed階層は単純に解凍されます。重要なケースはルール(a)です。次の宣言を考えてみましょう。クラス
Cはfinalであり、Iを実装しないため、IのインスタンスでもあるCのインスタンスは存在せず、これらは非結合です。したがって、CからIへの絞り込み参照変換はありません。対照的に、次の宣言を考えてみましょう。
クラス
DはJを実装しませんが、それでもDのインスタンスがJのインスタンスである可能性があります。たとえば、(場合によっては後で)次の宣言が行われている場合です。このため、
DはJと非結合ではなく、DからJへの絞り込み参照変換があります。CがIと非結合の場合、Iという名前のインタフェースはCという名前のクラスと非結合です。
C
<:Dではなく、D<:Cではない場合、Cという名前のクラスはDという名前の別のクラスと非結合です。I
<:Jではなく、J<:Iではなく、さらに次のケースのいずれかが該当する場合、Iという名前のインタフェースはJという名前の別のインタフェースと非結合です。Iは
sealedであり、Iの許容された直接サブクラスおよびサブインタフェースすべてがJと非結合です。Jは
sealedであり、IはJの許容された直接サブクラスおよびサブインタフェースすべてと非結合です。
このルールにより、2つの拡張インタフェース(9.1.1.4)が非結合であることはありません。
第6章: 名前
6.1 宣言
宣言により、エンティティがプログラムに導入され、このエンティティを参照するために名前内で使用できる識別子(3.8)が組み込まれます。この識別子には、導入されるエンティティがクラス、インタフェースまたは型パラメータである場合はTypeIdentifierであるという制約があります。
宣言されたエンティティは、次のいずれかです。
module宣言で宣言されたモジュール(7.7)package宣言で宣言されたパッケージ(7.4)単一型インポート宣言またはオンデマンド型インポート宣言(7.5.1、7.5.2)で宣言された、インポートされたクラスまたはインタフェース
単一静的インポート宣言またはオンデマンド静的インポート宣言で宣言された、インポートされた
staticメンバー(7.5.3、7.5.4)汎用クラス、インタフェース、メソッドまたはコンストラクタの宣言の一部として宣言された型パラメータ(8.1.2、9.1.2、8.4.4、8.8.4)
enum定数(8.9.1)
クラスまたはインタフェースのメソッド(8.4.1)、クラスのコンストラクタ(8.8.1)またはラムダ式(15.27.1)の仮パラメータ
try文のcatch句で宣言された例外ハンドラの例外パラメータ(14.20)ローカル変数で、次のいずれか:
ローカル・クラス(14.3)
コンストラクタ(8.8)も宣言によって導入されますが、新しい名前が導入されるのではなく、そのコンストラクタが宣言されたクラスの名前が使用されます。
汎用ではない型の宣言(class C ...)では、1つのエンティティ(非汎用型(C))が宣言されます。非汎用型は、構文上の類似点を除いてRAW型ではありません。対照的に、汎用型(class C<T> ...またはinterface C<T> ...)の宣言では、汎用型(C<T>)および対応する非汎用型(C)の2つのエンティティが宣言されます。この場合、用語Cの意味は、この用語が出現するコンテキストによって異なります。
汎用性が重要ではない場合、次の非汎用コンテキストに示すように、識別子
Cは非汎用型Cを示します。汎用性が重要である場合、非汎用コンテキストを除く6.5のすべてのコンテキストの場合のように、識別子
Cは次のどちらかを示します。
汎用クラスまたはインタフェース(
class C<T> ...またはinterface C<T> ...) (8.1.2、9.1.2)の宣言は、Cという名前のクラスと、RAWのC、C<Foo>、C<Bar>などの型のファミリの両方を導入します。非汎用コンテキストの1つとして次に示されている汎用性が重要ではない場所で
Cへの参照が発生した場合、Cへの参照は、クラスまたはインタフェースCを示します。その他のコンテキストでは、Cへの参照は、Cによって導入された型または型の一部を示します。
1415種類の非汎用コンテキストは、次のとおりです。
モジュール宣言内の
usesまたはprovidesディレクティブ内(7.7.1)単一型インポート宣言内(7.5.1)
単一静的インポート宣言内の
.の左側(7.5.3)オンデマンド静的インポート宣言内の
.の左側(7.5.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.1、14.20.3)
13.14.メソッドまたはコンストラクタのthrows句内(8.4.6、8.8.5、9.4)
14.15.例外パラメータ宣言内(14.20)最初の
1112の非汎用コンテキストは、6.5.1のTypeNameの最初の1112の構文コンテキストに相当します。12番目13番目の非汎用コンテキストは、静的メンバー・アクセスを表すためにC.xなどのExpressionNameにTypeNameCが含まれる可能性がある場所です。これら1213のコンテキストでTypeNameを一般的に使用することは重要です。これは、これらのコンテキストに型のファースト・クラス未満の使用が含まれることを示します。対照的に、13番目14番目と14番目15番目の非汎用コンテキストでは、ClassTypeが採用されます。これは、throwsおよびcatch句に、フィールド宣言などに合わせてファースト・クラス式の型が使用されることを示します。これら2つのコンテキストに非汎用としての特性が与えられるのは、例外型をパラメータ化できないという事実のためです(8.1.2)。ClassTypeプロダクションでは注釈が許可されるため、
throwsまたはcatch句内で型の使用に注釈を付けることができます。一方、TypeNameプロダクションでは注釈が許可されないため、単一型インポート宣言などの型の名前に注釈を付けることはできません。
...
6.5 名前の意味の確認
6.5.1 コンテキストに応じた名前の構文的分類
次のコンテキストでは、名前が構文的にModuleNameとして分類されます。
次のコンテキストでは、名前が構文的にPackageNameとして分類されます。
モジュール宣言内の
exportsまたはopensの右側修飾されたPackageName内の「
.」の左側
次のコンテキストでは、名前が構文的にTypeNameとして分類されます。
クラスまたはインタフェースを指定する場合:
モジュール宣言内の
usesまたはprovidesディレクティブ内(7.7.1)単一型インポート宣言内(7.5.1)
単一静的インポート宣言内の
.の左側(7.5.3)オンデマンド静的インポート宣言内の
.の左側(7.5.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)型が使用されている16のコンテキスト内のReferenceType (配列型内のカッコの左側にあるReferenceType、パラメータ化された型内の<の左側、パラメータ化された型の非ワイルドカード型引数内、またはパラメータ化された型のワイルドカード型引数の
extendsまたはsuper句内を含む)を構成する識別子または点線付きの識別子シーケンスとして(4.11):インタフェース宣言の
extends句内(9.1.3)汎用クラス、インタフェース、メソッドまたはコンストラクタの型パラメータ宣言の
extends句内(8.1.2、9.1.2、8.4.4、8.8.4)メソッドのレシーバ・パラメータの型(8.4)
例外パラメータ宣言内の型(14.20)
明示的なコンストラクタ呼出し文、クラス・インスタンス作成式、またはメソッド呼出し式に対する明示的な型引数リスト内(8.8.7.1、15.9、15.12)
非修飾クラス・インスタンス作成式内で、インスタンス化するクラス型として(15.9)、またはインスタンス化する無名クラスの直接スーパークラスまたは直接スーパーインタフェースとして(15.9.5)
配列作成式内の要素型(15.10.1)
キャスト式のキャスト演算子内の型(15.16)
instanceof関係演算子に続く型(15.20.2)メンバー・メソッドを検索するための参照型として、またはコンストラクトに対するクラス型または配列型としてのメソッド参照式内(15.13)。
前述の16のコンテキスト内のReferenceTypeの識別子からのTypeNameの抽出は、要素型や型引数などのReferenceTypeのサブ用語すべてに繰り返し適用することを意図しています。
たとえば、フィールド宣言に型
p.q.Foo[]が使用されるとします。配列型のカッコは無視され、用語p.q.Fooが識別子の点線シーケンスとして配列型内のカッコの左側に抽出され、TypeNameとして分類されます。後のステップで、p、qおよびFooのどれが型名またはパッケージ名であるかが確認されます。別の例として、キャスト演算子に型
p.q.Foo<? extends String>が使用されるとします。用語p.q.Fooが識別子用語の点線シーケンスとして再度、今回はパラメータ化された型内の<の左側に抽出され、TypeNameとして分類されます。用語Stringが、パラメータ化された型のワイルドカード型引数のextends句内の識別子として抽出され、TypeNameとして分類されます。
次のコンテキストでは名前がExpressionNameとして構文的に分類されています。
修飾されたスーパークラス・コンストラクタ呼び出し内で修飾する式として(8.8.7.1)
修飾されたクラス・インスタンス作成式内で修飾する式として(15.9)
配列アクセス式内で配列参照式として(15.10.3)
PostfixExpressionとして(15.14)
割当て演算子の左側のオペランドとして(15.26)
try-with-resources文内のVariableAccessとして(14.20.3)
次のコンテキストでは、名前が構文的にMethodNameとして分類されます。
- メソッド呼出し式内の「
(」の前(15.12)
次のコンテキストでは、名前が構文的にPackageOrTypeNameとして分類されます。
修飾されたTypeName内の「
.」の左側オンデマンド型インポート宣言内(7.5.2)
次のコンテキストでは、名前が構文的にAmbiguousNameとして分類されます。
修飾されたExpressionName内の「
.」の左側メソッド呼出し式内の「
(」の前にある右端の.の左側修飾されたAmbiguousName内の「
.」の左側注釈型要素宣言のデフォルト値句内(9.6.2)
要素と値のペア内の「
=」の右側(9.7.1)メソッド参照式内の
::の左側(15.13)
構文的分類の結果、特定の種類のエンティティを式の特定の部分に制限できるようになります。
フィールド、パラメータまたはローカル変数の名前を式として使用できます(15.14.1)。
メソッドの名前は、式内にメソッド呼出し式の一部としてのみ表示される場合があります(15.12)。
クラスまたはインタフェースの名前は、式内に、クラス・リテラルの一部(15.8.2)、修飾された
this式(15.8.4)、クラス・インスタンス作成式(15.9)、配列作成式(15.10.1)、キャスト式(15.16)、instanceof式(15.20.2)、enum定数(8.9)、あるいはフィールドまたはメソッドの修飾名の一部としてのみ出現できます。パッケージの名前は、式内にクラスまたはインタフェースの修飾名の一部としてのみ出現できます。
第8章: クラス
クラス宣言では、新しい参照型を定義し、これらの実装方法について記述します(8.1)。
最上位クラスは、ネストしたクラスではないクラスです。
ネストしたクラスは、宣言が別のクラスまたはインタフェースの本体内で行われるクラスです。
この章では、すべてのクラスの一般的なセマンティクスについて説明します。具体的には、最上位クラス(7.6)およびネストしたクラス(メンバー・クラス(8.5、9.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) public、protectedまたはprivateが含まれる場合があります。クラスのメンバーには、宣言されたメンバーと継承されたメンバーの両方が含まれます(8.2)。新しく宣言されたフィールドは、スーパークラスまたはスーパーインタフェース内で宣言されたフィールドを隠すことができます。新しく宣言されたクラス・メンバーおよびインタフェース・メンバーは、スーパークラスおよびスーパーインタフェース内で宣言されたクラス・メンバーまたはインタフェース・メンバーを隠すことができます。新しく宣言されたメソッドは、スーパークラスまたはスーパーインタフェース内で宣言されたメソッドを隠す、実装する、またはオーバーライドすることができます。
フィールド宣言(8.3)では、1回インカネーションされるクラス変数、およびクラスのインスタンスごとに新しくインカネーションされるインスタンス変数を記述します。フィールドはfinal (8.3.1.2)として宣言できます。この場合、このフィールドは1回のみ割り当てることができます。任意のフィールド宣言にイニシャライザを含めることができます。
メンバー・クラス宣言(8.5)では、前後のクラスのメンバーであるネストしたクラスを記述します。メンバー・クラスはstaticである場合があります。この場合、メンバー・クラスは前後のクラスのインスタンス変数にアクセスできません。または、内部クラス(8.1.3)である場合があります。
メンバー・インタフェース宣言(8.5)では、前後のクラスのメンバーであるネストしたインタフェースを記述します。
メソッド宣言(8.4)では、メソッド呼出し式(15.12)によって呼び出すことのできるコードを記述します。クラス・メソッドは、クラス型に関連して呼び出されます。インスタンス・メソッドは、クラス型のインスタンスである特定のオブジェクトに関連して呼び出されます。宣言が実装方法を示していないメソッドは、abstractとして宣言する必要があります。メソッドはfinal (8.4.3.3)として宣言される場合があります。この場合、このメソッドは隠したりオーバーライドすることができません。メソッドは、プラットフォーム依存のnativeコード(8.4.3.4)によって実装される場合があります。synchronizedメソッド(8.4.3.6)は、synchronized文(14.19)で使用されるように、本体を実行する前にオブジェクトを自動的にロックし、戻り時にオブジェクトを自動的にロック解除します。これにより、そのアクティビティを他のスレッド(17)のアクティビティと同期できるようになります。
メソッド名はオーバーロードする場合があります(8.4.9)。
インスタンス・イニシャライザ(8.6)は、作成時にインスタンスの初期化をサポートするために使用できる実行可能コードのブロックです(15.9)。
静的イニシャライザ(8.7)は、クラスの初期化をサポートするために使用できる実行可能コードのブロックです。
コンストラクタ(8.8)はメソッドと似ていますが、メソッド呼出しによって直接呼び出すことはできません。コンストラクタは、新しいクラス・インスタンスを初期化するために使用されます。メソッドと同様、これはオーバーロードする場合があります(8.8.8)。
8.1 クラス宣言
クラス宣言では、新しいクラスを指定します。
標準クラス宣言とenum宣言という2種類のクラス宣言があります。
- ClassDeclaration:
- NormalClassDeclaration
- EnumDeclaration
- NormalClassDeclaration:
- {ClassModifier}
classTypeIdentifier [TypeParameters]
[ClassExtends] [ClassImplements] [ClassPermits]
ClassBody
クラスは、クラス本体で終わるClassInstanceCreationExpression (15.9.5)またはEnumConstant (8.9.1)によっても暗黙的に宣言されます。
クラス宣言内のTypeIdentifierは、クラスの名前を指定します。
クラスの単純名が前後のクラスまたはインタフェースの単純名と同じである場合は、コンパイル時にエラーが発生します。
クラス宣言のスコープおよびシャドウ化については、6.3および6.4に規定されています。
8.1.1 クラス修飾子
クラス宣言には、クラス修飾子を含めることができます。
- ClassModifier:
- (次のうちの1つ)
- Annotation
publicprotectedprivate abstractstaticsealednon-sealedfinalstrictfp
クラス宣言の注釈修飾子に関するルールについては、9.7.4および9.7.5に規定されています。
アクセス修飾子public (6.6)は、最上位クラス(7.6)およびメンバー・クラス(8.5、9.5)のみに関連します。
修飾子protected、privateおよびstaticは、メンバー・クラスのみに関連します。
クラス宣言について同じキーワードが修飾子として複数回出現する場合、またはアクセス修飾子public、protectedおよびprivate (6.6)のうちの複数がクラス宣言に含まれる場合、コンパイル時にエラーが発生します。
クラス宣言に複数のクラス修飾子sealed、non-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を持ち、final、sealedまたはnon-sealedとして宣言されていない場合、コンパイル時にエラーになります。
enum型は、(暗黙的に)
finalまたはsealed(8.9)であるため、sealedインタフェースを実装できます。
JEP 395は、Javaプログラミング言語でレコード・クラスをサポートすることを提案しています。レコード・クラスは暗黙的にfinalであるため、sealedインタフェースを実装できます。
クラスが直接スーパークラスsealedまたは直接スーパーインタフェースsealedを持たないが、non-sealedとして宣言されている場合、コンパイル時にエラーになります。
sealedキーワードには、直接サブクラスがfinal、sealedまたはnon-sealedのいずれかであるかどうかとは関係なく、これらすべてを明示的に宣言するよう強制できるという効果があります。これにより、sealedクラス階層を誤って不要なサブクラス化に公開することが回避されます。
8.1.4 スーパークラス
標準クラス宣言内のオプションのextends句は、現在のクラスの直接スーパークラスの型を指定します。
- ClassExtends:
extendsClassType
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)によってのみ拡張可能なクラスEnumにClassTypeが名前を付けた場合は、コンパイル時にエラーが発生します。
ClassTypeには型引数があります。これは、整形式のパラメータ化された型(4.5)を表し、いずれの型引数もワイルドカード型引数にすることはできません。ワイルドカード型引数にすると、コンパイル時にエラーが発生します。
宣言にextends句が欠落しているクラスの直接スーパークラス型は次のとおりです。
クラス
Objectは直接スーパークラス型を持ちません。標準クラス宣言での
Object以外のクラスでは、直接スーパークラス型はObjectです。enumクラスEについては、直接スーパークラス型は
Enum<E>です。匿名クラスについては、直接スーパークラス型は15.9.5で定義されています。
クラスの直接スーパークラスは、その直接スーパークラスの型によって名前を付けられるクラスです。直接スーパークラスは、その実装が現在のクラスの実装の導出元であるクラスです。
スーパークラス関係は、直接スーパークラス関係の推移閉包です。次のいずれかを満たしている場合、クラスAはクラスCのスーパークラスです。
AがCの直接スーパークラスです
クラスBがCの直接スーパークラスであり、AがBのスーパークラスであり、この定義が再帰的に適用されます。
クラスは、その直接スーパークラスの直接サブクラスであり、その各スーパークラスのサブクラスであると言い表されます。
例8.1.4-1.直接スーパークラスおよびサブクラス
class Point { int x, y; }
final class ColoredPoint extends Point { int color; }
class Colored3DPoint extends ColoredPoint { int z; } // error
この場合、これらの関係は次のとおりです。
クラス
Pointは、Objectの直接サブクラスである。クラス
Objectは、クラスPointの直接スーパークラスである。クラス
ColoredPointは、クラスPointの直接サブクラスである。クラス
Pointは、クラスColoredPointの直接スーパークラスである。
クラスColored3dPointの宣言では、finalクラスColoredPointを拡張しようとするため、コンパイル時にエラーが発生します。
例8.1.4-2.スーパークラスおよびサブクラス
class Point { int x, y; }
class ColoredPoint extends Point { int color; }
final class Colored3dPoint extends ColoredPoint { int z; }
この場合、これらの関係は次のとおりです。
クラス
Pointは、クラスColoredPointのスーパークラスである。クラス
Pointは、クラスColored3dPointのスーパークラスである。クラス
ColoredPointは、クラスPointのサブクラスである。クラス
ColoredPointは、クラスColored3dPointのスーパークラスである。クラス
Colored3dPointは、クラスColoredPointのサブクラスである。クラス
Colored3dPointは、クラスPointのサブクラスである。
クラスCは、AがCのextendsまたはimplements句で、スーパークラスまたはスーパーインタフェースとして、またはスーパークラスまたはスーパーインタフェース名の完全修飾形式の修飾子として指定されている場合、クラスまたはインタフェースAに直接依存します。
クラスCは、次のいずれかが当てはまる場合、クラスまたはインタフェースAに依存します。
CがAに直接依存する。
Cが、A*に依存(9.1.3)するインタフェースIに直接依存する。
Cが、(この定義を再帰的に使用して) Aに依存するクラスDに直接依存する。
クラスがそれ自体に依存する場合は、コンパイル時にエラーが発生します。
クラスがロードされるときに、循環的に宣言されたクラスが実行時に検出された場合、ClassCircularityErrorがスローされます(12.2.1)。
例8.1.4-3.それ自体に依存するクラス
class Point extends ColoredPoint { int x, y; }
class ColoredPoint extends Point { int color; }
このプログラムを実行すると、クラスPointがそれ自体に依存しているため、コンパイル時にエラーが発生します。
8.1.5 スーパーインタフェース
クラス宣言内のオプションのimplements句は、宣言されるクラスの直接スーパーインタフェース型であるインタフェース型をリストします。
- ClassImplements:
implementsInterfaceTypeList- 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のスーパーインタフェースです。
IがCの直接スーパーインタフェースである。
Cが、9.1.3に示されている「インタフェースのスーパーインタフェース」の定義を使用して、Iがスーパーインタフェースである直接スーパーインタフェースJを持つ。
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; }
}
この場合、これらの関係は次のとおりです。
インタフェース
Paintableは、クラスPaintedPointのスーパーインタフェースです。インタフェース
Colorableは、クラスColoredPointおよびクラスPaintedPointのスーパーインタフェースです。9.1.3に定義されているとおり、インタフェース
PaintableはインタフェースColorableのサブインタフェースであり、ColorableはPaintableのスーパーインタフェースです。
クラスPaintedPointはスーパーインタフェースとしてColorableを持ちますが、これは、このスーパーインタフェースがColoredPointのスーパーインタフェースであると同時にPaintableのスーパーインタフェースであるためです。
例8.1.5-3.不正なインタフェースの複数継承
interface I<T> {}
class B implements I<Integer> {}
class C extends B implements I<String> {}
クラスCは、I<Integer>とI<String>の両方のサブタイプになろうとしているため、コンパイル時のエラーの原因となります。
...
8.1.6 許容された直接サブクラス
これは新しいサブセクションです既存のサブセクション8.1.6「クラス本体およびメンバー宣言」は8.1.7に番号が変更されており、それを参照している既存のすべての箇所は番号を変更する必要があります。
クラス宣言内のオプションのpermits句は、sealedクラス(8.1.1.2)を直接拡張することが許可されるクラスを指定します。
- ClassPermits
permitsTypeName {,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句によってリストされたクラスです。または、Cにpermits句がない場合は、Cと同じコンパイル・ユニット(7.3)で宣言された、正規名(6.7)を持ち、直接スーパークラスがCである各クラスです。
つまり、
sealedクラスCにpermits句がない場合は、Cを直接スーパークラスとしてリストする、同じコンパイル・ユニット内のすべての最上位およびメンバー・クラスを含むものが推測されます。正規名を持つという要件は、ローカル・クラスまたは匿名クラスは考慮されないことを意味します。
sealedクラスCの宣言にpermits句がなく、許容された直接サブクラスがない場合は、コンパイル時にエラーが発生します。
8.9 enumクラス
enum宣言では、少数の名前付きクラス・インスタンスのセットを定義する特別な種類のクラスである新しいenumクラスを指定します。
- EnumDeclaration:
- {ClassModifier}
enumTypeIdentifier [ClassImplements] EnumBody
enum宣言では、最上位enumクラス(7.6)またはメンバーenumクラス(8.5、9.5)を指定できます。
enum宣言に修飾子abstract、またはfinal、sealedまたはnon-sealedがある場合、コンパイル時にエラーになります。
enum宣言は、クラス本体を持つenum定数(8.9.1)が少なくとも1つ含まれないかぎり、暗黙的にfinalです。
enum宣言は、次のとおり、暗黙的にfinalであるか暗黙的にsealedです。
メンバーenumクラスは暗黙的にstaticです。メンバーenumクラスの宣言をstatic修飾子で重複して指定することが許可されています。
これは、内部クラスには定数変数を除き、
staticを含めることができないため、enumクラスを内部クラスのメンバーとして定義できないことを示しています(8.1.3)。
enum宣言について同じキーワードが修飾子として複数回出現する場合、またはアクセス修飾子public、protectedおよびprivate (6.6)のうちの複数がenum宣言に含まれる場合、コンパイル時にエラーが発生します。
enum宣言にはextends句はありません。enumクラスEの直接スーパークラス型はEnum<E>です(8.1.4)。
enumクラスには、そのenum定数で定義されたもの以外のインスタンスはありません。enumクラスを明示的にインスタンス化しようとすると、コンパイル時にエラーが発生します(15.9.1)。
コンパイル時のエラーに加え、さらに3つのメカニズムにより、enumクラスにはそのenum定数で定義されている以上のインスタンスがないことが確認されます。
Enumのfinalcloneメソッドによって、enum定数を一切クローニングできないことが確実化されます。enumクラスの反射型のインスタンス化は禁じられています。
直列化メカニズムによる特別な処理によって、直列化復元の結果として重複するインスタンスが作成されることがなくなります。
8.9.1 enum定数
enum宣言の本体には、enum定数を含めることができます。enum定数はenumクラスのインスタンスを定義します。
- EnumBody:
{[EnumConstantList] [,] [EnumBodyDeclarations]}- EnumConstantList:
- EnumConstant {
,EnumConstant} - EnumConstant:
- {EnumConstantModifier} Identifier [
([ArgumentList])] [ClassBody] - EnumConstantModifier:
- Annotation
便宜上、ここでは15.12の次のプロダクションを示します。
- ArgumentList:
- Expression {
,Expression}
enum定数宣言の注釈修飾子に関するルールについては、9.7.4および9.7.5に規定されています。
EnumConstantのIdentifierは、enum定数を参照するために名前で使用できます。
enum定数のスコープおよびシャドウ化については、6.3および6.4に規定されています。
enum定数の後に、引数(このセクションで後述するように、クラスの初期化中に定数が作成されるときにenumのコンストラクタに渡される)を指定できます。呼び出されるコンストラクタは、オーバーロード解決(15.12.2)の通常のルールを使用して選択されます。引数を省略すると、引数リストは空であるとみなされます。
enum定数のオプションのクラス本体は、直前と直後のenum型を拡張する匿名のクラス宣言(15.9.5)を暗黙的に定義します。enum定数のオプションのクラス本体は、(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.5、9.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}
interfaceTypeIdentifier [TypeParameters]
[InterfaceExtends] [InterfacePermits]
InterfaceBody
インタフェース宣言のTypeIdentifierでは、インタフェースの名前を指定します。
インタフェースの単純名がその包含クラスまたはインタフェースのいずれかと同じ場合、コンパイル時にエラーが発生します。
インタフェース宣言のスコープおよびシャドウ化については、6.3および6.4に規定されています。
9.1.1 インタフェース修飾子
インタフェース宣言にはインタフェース修飾子が含まれることがあります。
- InterfaceModifier:
- (次のうちの1つ)
- Annotation
publicprotectedprivate abstractstaticsealednon-sealedstrictfp
インタフェース宣言での注釈修飾子のルールは、9.7.4および9.7.5に規定されています。
修飾子protected、privateおよびstaticは、メンバー・インタフェース(8.5、9.5)のみに関連します。
同一のキーワードがあるインタフェース宣言の1つの修飾子として複数回出現する、またはインタフェース宣言にアクセス修飾子public、protected、およびprivateのいずれかが複数出現する場合は、コンパイル時にエラーが発生します(6.6)。
インタフェースがsealedとnon-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:
extendsInterfaceTypeList
便宜上、ここでは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のスーパーインタフェースです。
KがIの直接スーパーインタフェースである。
JがIの直接スーパーインタフェースである場合、KはJのスーパーインタフェースであり、この定義が再帰的に適用される。
インタフェースは、その直接スーパーインタフェースの直接サブインタフェース、およびそのスーパーインタフェースそれぞれのサブインタフェースであると言い表されます。
すべてのクラスがクラスObjectの拡張ですが、すべてのインタフェースが拡張である単一のインタフェースは存在しません。
インタフェースIは、AがIのextends句で、スーパーインタフェースとして、またはスーパーインタフェース名の完全修飾形式の修飾子として指定されている場合、クラスまたはインタフェースAに直接依存します。
インタフェースIは、次のいずれかが当てはまる場合、クラスまたはインタフェースAに依存します。
IがAに直接依存する。
Aに依存するクラスCにIが直接依存する(8.1.5)。
(この定義を再帰的に使用して) Aに依存するインタフェースJにIが直接依存する。
インタフェースがそれ自体に依存する場合、コンパイル時にエラーが発生します。
インタフェースがロードされるときに、循環的に宣言されたインタフェースが実行時に検出された場合、ClassCircularityErrorがスローされます(12.2.1)。
9.1.4 許容された直接サブクラスおよびサブインタフェース
これは新しいサブセクションです既存のサブセクション9.1.4「インタフェース本体およびメンバー宣言」は9.1.5に番号が変更されており、それを参照している既存のすべての箇所は番号を変更する必要があります。
インタフェース宣言内のオプションのpermits句は、sealedインタフェース(9.1.1.4)を直接実装または拡張することが許可されているクラスおよびインタフェースをリストします。
- InterfacePermits
permitsTypeName {,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.5、9.1.3)ではない場合、コンパイル時にエラーになります。
sealedインタフェースIが名前付きクラスに属する場合、Iの宣言のpermits句で名前が付けられたすべてのクラスまたはインタフェースが、Iと同じモジュールに属する必要があります。そうでない場合、コンパイル時にエラーになります。
sealedインタフェースIが名前付きでないモジュールに属する場合、Iの宣言のpermits句で名前が付けられたすべてのクラスまたはインタフェースが、Iと同じパッケージに属する必要があります。そうでない場合、コンパイル時にエラーになります。
sealedインタフェース階層は、様々なメンテナンス・ドメインにわたって宣言されることを意図していません。モジュールは循環方式で相互に依存することはできませんが、
sealedインタフェースとその直接サブクラスおよびサブインタフェースは循環方式で相互に依存する必要があります(permits、implementsおよびextends句のそれぞれ)。したがって、必然的に、sealedインタフェースとその直接サブクラスおよびサブインタフェースが同じモジュール内で共存する必要があります。名前が付けられていないモジュールでは、sealedインタフェースとその直接サブクラスおよびサブインタフェースが同じパッケージに属する必要があります。
sealedインタフェースIの許容された直接サブクラスおよびサブインタフェースは、そのpermits句によってリストされたクラスおよびインタフェースです。または、Iにpermits句がない場合は、Iと同じコンパイル・ユニット(7.3)で宣言された、正規名(6.7)を持ち、直接スーパーインタフェースにIが含まれる各最上位またはメンバー・クラスまたはインタフェースです。
sealedインタフェースIの宣言にpermits句がなく、許容された直接サブクラスまたはサブインタフェースがない場合は、コンパイル時にエラーが発生します。
9.6 注釈インタフェース
注釈宣言は、特殊な種類のインタフェースである新しい注釈インタフェースを指定します。注釈宣言は標準インタフェース宣言と区別するため、キーワードinterfaceの前にアットマーク(@)が付いています。
- AnnotationDeclaration:
- {InterfaceModifier}
@interfaceTypeIdentifier 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メソッド・セットにします。これにより、次の両方が当てはまるメソッドmがM内に存在する場合、Iは関数型インタフェースになります。
クラスを宣言およびインスタンス化してインタフェース・インスタンスを作成する通常のプロセス(15.9)以外に、メソッド参照式とラムダ式(15.13、15.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されるため、インタフェースを実装する唯一の方法は、クラスが非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のメンバーではないabstractメソッドを1つのみ宣言するとともに、Objectのpublicメンバーではない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.mはX.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.mはJ.mおよびK.mに戻り型置換可能ですが、単一のメソッドを使用して関数型インタフェース型Functional<String,Integer>を実装できないことは明確です。ただし、関数型インタフェースである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)。
第13章: バイナリ互換性
13.4 クラスの展開
13.4.2 sealed、non-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クラスCにsealed直接スーパークラスまたはsealed直接スーパーインタフェースがある場合、sealed修飾子を単純に削除することは、Cがコンパイルされないことを意味します。sealed直接スーパークラスまたはsealed直接スーパーインタフェースを持つすべてのクラスは、final、sealedまたは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直接スーパーインタフェースを持つすべてのクラスは、final、sealedまたはnon-sealed(8.1.1.2)である必要があるためです。
13.4.2.3 finalクラス
finalとして宣言されなかったクラスがfinalとして宣言されるよう変更される場合、finalクラスはサブクラスを持つことができないため、このクラスの既存のサブクラスのバイナリがロードされると、VerifyErrorがスローされます。このような変更を広範に配布されるクラスに対して行うことはお薦めしません。
finalとして宣言されたクラスがfinalとして宣言されないよう変更される場合、既存のバイナリとの互換性が失われることはありません。
sealed直接スーパークラスまたはsealed直接スーパーインタフェースを持たないクラスからfinal修飾子を削除しても、既存のバイナリとの互換性が失われることはありません。
finalクラスCにsealed直接スーパークラスまたはsealed直接スーパーインタフェースがある場合、final修飾子を単純に削除することは、Cがコンパイルされないことを意味します。sealed直接スーパークラスまたはsealed直接スーパーインタフェースを持つすべてのクラスは、final、sealedまたは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インタフェースIにsealed直接スーパーインタフェースがある場合、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 ローカル・クラスおよびインタフェースの宣言
ローカル・クラスまたはローカル・インタフェースは、ネストしたクラスまたはインタフェース(8、9)であり、その宣言はブロックに直接含まれます(14.2)。
- LocalClassOrInterfaceDeclaration:
- ClassDeclaration
- NormalInterfaceDeclaration
ローカル・クラスはenumクラス(8.9)である場合があります。ローカル・インタフェースが注釈インタフェース(9.6)であることはありません。
ローカル・クラスおよびインタフェース宣言は、ブロック内の文と自由に混在できます。
ローカル・クラスまたはインタフェースは、パッケージ、クラスまたはインタフェースのメンバーではありません。無名クラス(15.9.5)とは異なり、ローカル・クラスまたはインタフェースには単純名があります(6.2, 6.7)。
ローカルenumクラスおよびローカル・インタフェースは暗黙的にstaticです(8.1.1.4、9.1.1.3)。暗黙的にstaticでないローカル・クラスは内部クラスです(8.1.3)。
ローカル・クラスまたはインタフェースがアクセス修飾子public、protectedまたは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で示されたクラスまたはインタフェースが汎用ではない場合、コンパイル時にエラーが発生します。
クラス・インスタンス作成式がクラス本体で終わる場合、インスタンス化対象のクラスは匿名クラスです。次に、
クラス・インスタンス作成式が修飾されていない場合、次のようになります。
ClassOrInterfaceTypeToInstantiate内のIdentifierは、アクセス可能であり、非
final、非sealedであり、enumクラスではないクラスか、アクセス可能かつ非sealed(6.6)のインタフェースを表す必要があります。そうでない場合、コンパイル時にエラーが発生します。ClassOrInterfaceTypeToInstantiate内のIdentifierがクラス、Cを表す場合、Cの匿名直接サブクラスが宣言されます。TypeArgumentsが存在する場合、Cは、TypeArgumentsによって与えられる型引数を持ちます。
<>が存在する場合、Cは、15.9.3で推測されている型引数を持ちます。そうでない場合、Cは型引数を持ちません。サブクラスの本体は、クラス・インスタンス作成式で与えられるClassBodyです。インスタンス化対象のクラスは匿名サブクラスです。ClassOrInterfaceTypeToInstantiate内のIdentifierがインタフェース、Iを表す場合、Iを実装する
Objectの匿名直接サブクラスが宣言されます。TypeArgumentsが存在する場合、Iは、TypeArgumentsによって与えられる型引数を持ちます。<>が存在する場合、Iは、15.9.3で推測されている型引数を持ちます。そうでない場合、Iは型引数を持ちません。サブクラスの本体は、クラス・インスタンス作成式で与えられるClassBodyです。インスタンス化対象のクラスは匿名サブクラスです。クラス・インスタンス作成式が修飾される場合、次のようになります。
ClassOrInterfaceTypeToInstantiate内のIdentifierは、アクセス可能であり、非
final、非sealedであり、enumクラスではなく、Primary式またはExpressionNameのコンパイル時型のメンバーである内部クラスを一義的に表す必要があります。そうでない場合、コンパイル時にエラーが発生します。ClassOrInterfaceTypeToInstantiate内のIdentifierがクラス、Cを表すようにします。Cの匿名直接サブクラスが宣言されます。TypeArgumentsが存在する場合、Cは、TypeArgumentsによって与えられる型引数を持ちます。
<>が存在する場合、Cは、15.9.3で推測されている型引数を持ちます。そうでない場合、Cは型引数を持ちません。サブクラスの本体は、クラス・インスタンス作成式で与えられるClassBodyです。インスタンス化対象のクラスは匿名サブクラスです。
クラス・インスタンス作成式が匿名クラスを宣言しない場合、次のようになります。
クラス・インスタンス作成式が修飾されていない場合、次のようになります。
ClassOrInterfaceTypeToInstantiate内のIdentifierは、アクセス可能であり、非
abstractであり、enumクラスではないクラスを表す必要があります。そうでない場合、コンパイル時にエラーが発生します。インスタンス化対象のクラスは、ClassOrInterfaceTypeToInstantiate内のIdentifierによって指定されます。TypeArgumentsが存在する場合、クラスは、TypeArgumentsによって与えられる型引数を持ちます。
<>が存在する場合、クラスは、15.9.3で推測されている型引数を持ちます。そうでない場合、クラスは型引数を持ちません。クラス・インスタンス作成式が修飾される場合、次のようになります。
ClassOrInterfaceTypeToInstantiateは、アクセス可能であり、非
abstractであり、enumクラスではなく、Primary式またはExpressionNameのコンパイル時型のメンバーである内部クラスを一義的に表す必要があります。インスタンス化対象のクラスは、ClassOrInterfaceTypeToInstantiate内のIdentifierによって指定されます。TypeArgumentsが存在する場合、クラスは、TypeArgumentsによって与えられる型引数を持ちます。
<>が存在する場合、クラスは、15.9.3で推測されている型引数を持ちます。そうでない場合、クラスは型引数を持ちません。
15.9.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が明示的に注釈として付けられていない場合)、気付かれずに間違った状態になっているプログラムを回避しやすくなります。