このドキュメントでは、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}
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.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:
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)によってのみ拡張可能なクラス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:
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のスーパーインタフェースです。
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
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
句によってリストされたクラスです。または、Cにpermits
句がない場合は、Cと同じコンパイル・ユニット(7.3)で宣言された、正規名(6.7)を持ち、直接スーパークラスがCである各クラスです。
つまり、
sealed
クラスCにpermits
句がない場合は、Cを直接スーパークラスとしてリストする、同じコンパイル・ユニット内のすべての最上位およびメンバー・クラスを含むものが推測されます。正規名を持つという要件は、ローカル・クラスまたは匿名クラスは考慮されないことを意味します。
sealed
クラスCの宣言にpermits
句がなく、許容された直接サブクラスがない場合は、コンパイル時にエラーが発生します。
8.9 enumクラス
enum宣言では、少数の名前付きクラス・インスタンスのセットを定義する特別な種類のクラスである新しいenumクラスを指定します。
- EnumDeclaration:
- {ClassModifier}
enum
TypeIdentifier [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
のfinal
clone
メソッドによって、enum定数を一切クローニングできないことが確実化されます。enumクラスの反射型のインスタンス化は禁じられています。
直列化メカニズムによる特別な処理によって、直列化復元の結果として重複するインスタンスが作成されることがなくなります。
8.9.1 enum定数
enum宣言の本体には、enum定数を含めることができます。enum定数はenumクラスのインスタンスを定義します。
- EnumBody:
{
[EnumConstantList] [,
] [EnumBodyDeclarations]}
- EnumConstantList:
- EnumConstant {
,
EnumConstant} - EnumConstant:
- {EnumConstantModifier} Identifier [
(
[ArgumentList])
] [ClassBody] - EnumConstantModifier:
- Annotation
便宜上、ここでは15.12の次のプロダクションを示します。
- ArgumentList:
- Expression {
,
Expression}
enum定数宣言の注釈修飾子に関するルールについては、9.7.4および9.7.5に規定されています。
EnumConstantのIdentifierは、enum定数を参照するために名前で使用できます。
enum定数のスコープおよびシャドウ化については、6.3および6.4に規定されています。
enum定数の後に、引数(このセクションで後述するように、クラスの初期化中に定数が作成されるときにenumのコンストラクタに渡される)を指定できます。呼び出されるコンストラクタは、オーバーロード解決(15.12.2)の通常のルールを使用して選択されます。引数を省略すると、引数リストは空であるとみなされます。
enum定数のオプションのクラス本体は、直前と直後のenum型を拡張する匿名のクラス宣言(15.9.5)を暗黙的に定義します。enum定数のオプションのクラス本体は、(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}
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に規定されています。
修飾子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:
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のスーパーインタフェースです。
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
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.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}
@
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
メソッド・セットにします。これにより、次の両方が当てはまるメソッド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
が明示的に注釈として付けられていない場合)、気付かれずに間違った状態になっているプログラムを回避しやすくなります。