このドキュメントでは、switch
のパターン・マッチング(Java SE 18のプレビュー機能)をサポートするためにJava言語仕様に加えられた変更について説明します。この機能の概要は、JEP 420を参照してください。
変更は、JLSの既存のセクションについて説明しています。新しいテキストはこのように示され、削除されたテキストはこのように示されます。必要に応じて、説明と考察が端の方にグレーのボックスで囲まれて記載されています。
変更ログ:
2021-12-08: 細かいタイポを修正しました。
2021-11-11: フィードバックに従って軽微な変更を行いました。
14.11.1 switchブロックの網羅性の定義を強化しました
13.4.2.1、13.4.26および13.5.2: sealed階層およびenumクラスに対する変更により、プリコンパイルされた網羅的なswitchの実行時に例外が発生する仕組みを説明する新しい文章を追加しました。
2021-10-20: 初稿をリリースしました。様々なバグ修正に加えて、JEP 405のプレビュー機能からの主な変更点は次のとおりです。
第6章: 名前
6.3 宣言のスコープ
6.3.1 式のパターン変数のスコープ
6.3.1.6 switch
式
次のルールルールは、switchルールで構成されたswitchブロック(14.11.1)があるswitch
式(15.28)に適用されます。
switchラベルによって導入されるパターン変数は、関連付けられたswitchルール式、switchルール・ブロックまたはswitchルール
throw
文で明確に照合されます。switchラベルによって導入されるいずれかのパターン変数が、関連付けられたswitchルール式、switchルール・ブロックまたはswitchルール
throw
文でスコープ内にすでにある場合、コンパイル時にエラーになります。
次のルールは、switchラベルが付いた文グループで構成されたswitchブロック(14.11.1)があるswitch式に適用されます。
switchラベルによって導入されるパターン変数は、switchラベルが付いた文グループのすべての文で明確に照合されます。
switchラベルによって導入されるいずれかのパターン変数が、switchラベルが付いた文グループの文でスコープ内にすでにある場合、コンパイル時にエラーになります。
- switchラベルが付いた文グループ
(14.11.1)に含まれる文Sによって導入されるパターン変数は、このswitchラベルが付いた文グループ内のSに続くすべての文(存在する場合)で明確に照合されます。
6.3.2 文のパターン変数のスコープ
6.3.2.6 switch
文
次のルールルールは、switchルールで構成されたswitchブロック(14.11.1)があるswitch
文(14.11)に適用されます。
switchラベルによって導入されるパターン変数は、関連付けられたswitchルール式、switchルール・ブロックまたはswitchルール
throw
文で明確に照合されます。switchラベルによって導入されるいずれかのパターン変数が、関連付けられたswitchルール式、switchルール・ブロックまたはswitchルール
throw
文でスコープ内にすでにある場合、コンパイル時にエラーになります。
次のルールは、switchラベルが付いた文グループで構成されたswitchブロック(14.11.1)があるswitch式に適用されます。
switchラベルによって導入されるパターン変数は、switchラベルが付いた文グループのすべての文で明確に照合されます。
switchラベルによって導入されるいずれかのパターン変数が、switchラベルが付いた文グループの文でスコープ内にすでにある場合、コンパイル時にエラーになります。
- switchブロック文グループ
(14.11.1)に含まれるラベルが付いた文Sによって導入されるパターン変数は、switchブロック文グループ内のSに続くすべての文(存在する場合)で明確に照合されます。
6.3.3 パターンのパターン変数のスコープ
6.3.3.1 ガード付きパターン
次のルールは、ガード付きパターンp && e
に適用されます。
p
によって宣言されるパターン変数は、e
で明確に照合されます。p
によって宣言されるパターン変数がe
でスコープ内にすでにある場合、コンパイル時にエラーになります。
6.3.4 Switchラベルのパターン変数のスコープ
パターン変数は、パターンを持つswitchラベルによって導入でき、関連付けられたswitch
式(6.3.1.6)またはswitch
文(6.3.2.6)の関連部分のスコープにあります。
次のルールは、switchラベルに適用されます。
- パターン変数は、pによって宣言される場合、パターンpのパターンのcaseラベル要素を持つswitchラベルによって導入されます。
第13章: バイナリ互換性
13.4 クラスの展開
13.4.2 sealed
、non-sealed
およびfinal
クラス
13.4.2.1 sealed
クラス
自由に拡張可能であったクラス(8.1.1.2)がsealed
として宣言されるように変更された場合、このクラスの既存のサブクラスのバイナリがロードされ、そのサブクラスがこのクラスの許容された直接サブクラス(8.1.6)でない場合には、IncompatibleClassChangeError
がスローされます。広く配布されるクラスについては、このような変更はお薦めしません。
final
として宣言されたクラスをsealed
として宣言されるよう変更しても、既存のバイナリとの互換性が失われることはありません。
sealed
クラスの許容された直接サブクラスのセットにクラスを追加しても、既存のバイナリとの互換性が失われることはありません。
許容された直接サブクラスを追加して
sealed
クラスを展開することは、バイナリ互換性がある変更とみなされます。以前にエラーなしでリンクされていた既存のバイナリ(網羅的なswitch
(14.11.1)を含むクラス・ファイルなど)は引き続きエラーなしでリンクされるためです。網羅的なswitch
を含むクラス・ファイルは、それが切り替えたsealed
クラスが新しい許容された直接サブクラスを持つように階層の所有者によって拡張されても、リンクに失敗することはありません。JVMは、網羅的なswitch
を含むクラス・ファイルをリンクするときに網羅性チェックを実行する必要はありません。
コンパイル時に認識されていなかった、許容された直接サブクラスのインスタンスが検出された場合、網羅的な
switch
の実行はリンケージ・エラーで失敗することがあります(IncompatibleClassChangeError
がスローされます) (14.11.3、15.28.2)。厳密に言えば、このリンケージ・エラーは、sealed
クラスのバイナリ互換性がない変更を示しているのではなく、より正確にはsealed
クラスの移行互換性がない変更を示しています。
sealed
クラスの許容された直接サブクラスのセットからクラスが削除された場合、削除されたクラスの既存のバイナリがロードされると、IncompatibleClassChangeError
がスローされます。
sealed
直接スーパークラスまたはsealed
直接スーパーインタフェースを持たないクラスからsealed
修飾子を削除しても、既存のバイナリとの互換性が失われることはありません。
sealed
直接スーパークラスまたはsealed
直接スーパーインタフェースを持つすべてのクラスはfinal
、sealed
またはnon-sealed
である必要があるため、sealedクラスCがsealed
直接スーパークラスまたはsealed
直接スーパーインタフェースを持っていた場合、sealed
修飾子を削除すると、Cを再コンパイルできなくなります。
13.4.26 enumクラスの展開
enumクラス内でenum定数を追加したり、並べ替えても、既存のバイナリとの互換性が失われることはありません。
sealed
クラス(13.4.2.1)と同様に、enumクラスにenum定数を追加することは、バイナリ互換性がある変更とみなされますが、コンパイル時に認識されていなかった新しいenum定数がswitch
によって検出された場合、網羅的なswitch
(14.11.1)の実行はリンケージ・エラーで失敗することがあります(IncompatibleClassChangeError
がスローされることがあります) (14.11.3、15.28.2)。
enumクラスからenum定数を削除すると、そのenum定数に対応するpublic
フィールド(8.9.3)が削除されます。この結果は、13.4.8に明示されています。広く配布されるenumクラスについては、このような変更はお薦めしません。
他のあらゆる点において、enumクラスに関するバイナリ互換性のルールは標準クラスに関するものと同じです。
13.5 インタフェースの展開
13.5.2 sealed
およびnon-sealed
インタフェース
自由に拡張可能であったインタフェース(9.1.1.4)がsealed
として宣言されるように変更された場合、このインタフェースの既存のサブクラスまたはサブインタフェースのバイナリがロードされ、そのサブクラスまたはサブインタフェースがこのインタフェースの許容された直接サブクラスまたはサブインタフェース(9.1.4)でない場合には、IncompatibleClassChangeError
がスローされます。広く配布されるクラスについては、このような変更はお薦めしません。
sealed
インタフェースの許容された直接サブクラスまたはサブインタフェースのセットにそれぞれクラスまたはインタフェースを追加しても、既存のバイナリとの互換性が失われることはありません。
sealed
クラス(13.4.2.1)と同様に、sealed
インタフェースの許容された直接サブクラスまたはサブインタフェースを追加することは、バイナリ互換性がある変更とみなされますが、コンパイル時に認識されていなかった新しい許容された直接サブクラスおよびサブインタフェースのインスタンスがswitch
によって検出された場合、網羅的なswitch
(14.11.1)の実行はリンケージ・エラーで失敗することがあります(IncompatibleClassChangeError
がスローされることがあります) (14.11.3、15.28.2)。
sealed
インタフェースの許容された直接サブクラスまたはサブインタフェースのセットからクラスまたはインタフェースが削除された場合、削除されたクラスまたはインタフェースの既存のバイナリがロードされると、IncompatibleClassChangeError
がスローされます。
sealed
として宣言されたインタフェースをnon-sealed
として宣言されるように変更しても、既存のバイナリとの互換性が失われることはありません。
non-sealed
インタフェースIには、sealed
直接スーパーインタフェースが必要です。sealed
直接スーパーインタフェースを持つすべてのインタフェースはsealed
またはnon-sealed
である必要があるため、non-sealed
修飾子を削除すると、Iを再コンパイルできなくなります。
sealed
直接スーパーインタフェースを持たないインタフェースからsealed
修飾子を削除しても、既存のバイナリとの互換性が失われることはありません。
sealed
直接スーパーインタフェースを持つすべてのインタフェースはsealed
またはnon-sealed
である必要があるため、sealedインタフェースIがsealed
直接スーパーインタフェースを持っていた場合、sealed
修飾子を削除すると、Iを再コンパイルできなくなります。
第14章: ブロックと文およびパターン
14.11 switch
文
switch
文は、式の値に応じて、いくつかの文または式の1つに制御を転送します。
- SwitchStatement:
switch
(
Expression)
SwitchBlock
Expressionはセレクタ式と呼ばれます。セレクタ式の型はchar
、byte
、short
、int
、Character
、Byte
、Short
、Integer
、String
またはenum型(8.9)である必要があり、そうでない場合、コンパイル時にエラーが発生します。
セレクタ式の型のこれらの制限は、次のセクションに定義されている、セレクタ式と互換性があるというswitchブロックの概念に含まれるようになりました。
14.11.1 Switchブロック
switch
文およびswitch
式(15.28)の両方の本体は、switchブロックと呼ばれます。このサブセクションでは、switch
文またはswitch
式のどちらに出現するかに関係なく、すべてのswitchブロックに適用される一般的なルールを示します。他のサブセクションでは、switch
文のswitchブロック(14.11.2)またはswitch
式のswitchブロック(15.28.1)のいずれかに適用される追加ルールを示します。
- SwitchBlock:
{
SwitchRule {SwitchRule}}
{
{SwitchBlockStatementGroup} {SwitchLabel:
}}
- SwitchRule:
- SwitchLabel
->
Expression;
- SwitchLabel
->
Block - SwitchLabel
->
ThrowStatement - SwitchBlockStatementGroup:
- SwitchLabel
:
{SwitchLabelBlockStatements:
}
- SwitchLabel:
case
CaseConstant {,
CaseConstant}default
- SwitchLabel:
- CaseOrDefaultLabel {
:
CaseOrDefaultLabel } - CaseOrDefaultLabel:
case
CaseLabelElement {,
CaseLabelElement }default
- CaseLabelElement:
- CaseConstant
- Pattern
null
default
- CaseConstant:
- ConditionalExpression
switchブロックは次のいずれかで構成できます。
Switchルール。これは、
->
を使用して、switchルール式、switchルール・ブロックまたはswitchルールthrow
文を導入します。またはSwitchラベルが付いた文グループ。これは
:
を使用してswitchラベルが付いたブロック文を導入します。
すべてのswitchルールおよびswitchラベルが付いた文グループは、switchラベルで開始し、これは1つ以上のcase
ラベルまたはdefault
ラベルのいずれかです。case
またはdefault
ラベルを使用します。case
ラベルには、1つ以上のcaseラベル要素があります。複数のswitchラベルが、1つのswitchラベルが付いた文グループに許可されます。switchラベルは、caseラベル要素があるcase
ラベルを使用する場合、caseラベル要素を持ちます。
case
ラベルには、1つ以上のcase
定数があります。すべてのcase
定数は、定数式(15.29)またはenum定数の名前(8.9.1)のいずれかである必要があり、そうでない場合、コンパイル時にエラーが発生します。
Switchラベルおよびそのcase
定数は、switchブロックに関連付けられていると言い表されます。switchブロックに関連付けられている2つのcase
定数が同じ値を持つことはなく、そうでない場合、コンパイル時にエラーが発生します。
switchラベルがswitchブロックの最後に表示される場合、複数のcase
またはdefault
ラベルで構成されているときは、コンパイル時にエラーになります。
switchルールのswitchラベルが複数のcase
またはdefault
ラベルで構成されている場合、コンパイル時にエラーになります。
つまり、
case 1: case 2 -> ...
は有効なswitchルールではありませんが、case 1, 2 -> ...
と書くことができます。
switchブロックのすべてのswitchラベルについて、次のすべてが当てはまる必要があり、そうでない場合、コンパイル時にエラーが発生します。
switchラベルに定数のcaseラベル要素がある場合、それは定数式(15.29)またはenum定数の名前(8.9.1)のいずれかです。
switchラベルは、同じ値の複数の定数のcaseラベル要素を持つことができません。
switchラベルに定数のcaseラベル要素がある場合、switchラベルに他のcase要素ラベルもあるときは、それらは定数のcaseラベル要素、
default
caseラベル要素、またはnull
caseラベル要素のいずれかである必要があります。switchラベルでは、複数の
default
ラベルを使用できません。switchラベルは、複数の
default
caseラベル要素を持つことができません。switchラベルは、
default
caseラベル要素を持ち、かつdefault
ラベルを使用することはできません。switchラベルは、複数の
null
caseラベル要素を持つことができません。switchラベルに
null
caseラベル要素があり、switchラベルにパターンのcase要素ラベルもある場合、それらは型パターン(14.30.1)である必要があります。switchラベルは、複数のパターンのcaseラベル要素を持つことができません。
switchラベルは、パターンのcaseラベル要素を持ち、かつ
default
ラベルを使用することはできません。switchラベルは、パターンのcaseラベル要素および
default
caseラベル要素の両方を持つことはできません。
これらのルールはswitchラベルの形式を制限します。複雑さの多くは、文グループのswitchラベル内のcaseラベル要素を結合する2つの方法のサポートが原因です(たとえば、
case 1: case 2
およびcase 1,2
)。
switchラベルは、default
ラベルを使用するかdefault
caseラベル要素がある場合、default switchラベルと呼ばれます。
switchラベルは、両方のswitchラベルが適用される値があり、明白なプリファレンスがない場合、別のswitchラベルより優先されると言い表されます。優先を決定するルールは次のとおりです。
定数のcaseラベル要素を持つswitchラベルは、同じ定数のcaseラベル要素を持つ別のswitchラベルより優先されます。
これは、セレクタ式の値が
2
のときにどちらのswitchラベルが一致するか、そうでない場合は明確でない次のような例を無視しています。int i = ...; switch (i) { case 1, 2 -> System.out.println("1 or 2"); case 2, 3 -> System.out.println("2 or 3"); // Error! }
パターンのcaseラベル要素pを持つswitchラベルは、pがqより優先される場合、パターンのcaseラベル要素qを持つ別のswitchラベルより優先されます(14.30.3)。
パターンが別のパターンより優先される定義は、それらの型に基づきます。たとえば、次はコンパイル時にエラーになります。
Object o = ... switch (o) { case Object obj -> System.out.println("Object"); case String s -> System.out.println("String"); // Error - dominated switch label }
正確に言うと、優先はパターンの型のイレイジャに関して定義されます。たとえば、型パターン
List<String> ls
は型パターンList<Integer> li
より優先され、逆も同様です。つまり、たとえば、次のswitchブロックはコンパイル時にエラーになります。Object o = ...; switch (o) { case List<Integer> li -> ... case List<String> ls -> ... // Error - dominated switch label ... }
優先は、ガード付きパターンの後ろにそのガードなしの形式が続くことを許可します。
Object o = ...; switch (o) { case List l && l.length() > 2 -> ... case List l -> ... // List of length <= 2 ... }
包含
switch
文またはswitch
式のセレクタ式の型の合計であるパターンのcaseラベル要素pを持つswitchラベルは、null
caseラベル要素を持つswitchラベルより優先されます。パターンのcaseラベル要素pが定数のcaseラベル要素cより優先される場合、pを持つswitchラベルは、cを持つ別のswitchラベルより優先されます。
次のいずれかが当てはまる場合、パターンpは定数cより優先されます。
pは型パターンであり、次のいずれかが当てはまります。
型cはプリミティブ型であり、そのラッパー・クラス(5.1.7)は型pのイレイジャのサブタイプです。
型cは参照型であり、型pのイレイジャのサブタイプです。
このルールの最初の部分は、プリミティブ定数のcaseラベルが優先される場合を対象とします。
Object o = ...; switch (o) { case Integer i -> ... case 42 -> ... // Error - dominated! ... }
このルールの2番目の部分は、enum定数のcaseラベルが優先される場合を対象とします。
enum E { A, B, C } Object o = ...; switch (o) { case E e -> ... case A -> ... // Error - dominated! ... }
pはカッコ付きのパターンであり、含まれているパターンはcより優先されます。
pはガード付きパターンであり、含まれているパターンはcより優先されます。
ガード付きパターンのcaseラベルは、含まれているパターンが優先される場合、定数のcaseラベルより優先されます。ガーディング式の分析(通常は決定不能)は行われません。したがって、ガード付きパターンのcaseラベルより前に定数のcaseラベルが出現する必要があります。次に例を示します。
Object o = ...; switch (o) { case 42 -> ... case Integer i && i < 50 -> ... // All integers less than 50 // *except* 42 case Integer i -> ... // All other integers ... }
switchブロック内のswitchラベルが、switchブロック内の後続のswitchラベルより優先される場合、コンパイル時にエラーになります。
次の両方が当てはまるswitchラベルが付いた文グループで構成されるswitchブロックに文がある場合、コンパイル時にエラーになります。
パターンがパターン変数を導入するパターンのcaseラベル要素を持つswitchラベルでラベル付けされています。
switchブロック内で前に文があり、その文を正常に完了できます(14.22)。
この条件は、switchラベルで宣言されたパターン変数がスコープ内にあるが、成功したパターン・マッチングがない、switchラベルが付いた文に到達する可能性を除外するために必要です。たとえば、型パターン
Integer i
を持つswitchラベルでラベル付けされた文に、先行する文グループから到達できるため、パターン変数i
は初期化されません。Object o = "Hello"; switch (o) { case String s: System.out.println("String: " + s ); case Integer i: System.out.println(i + 1); // Error! Can be reached // without matching switch label }
次の両方が当てはまる場合、switchブロック内のswitchラベルで使用されるすべてのcase
ラベルがeと互換性がある場合、switch
文またはswitch
式のswitchブロックは、型セレクタ式T eと互換性があります。次のように、case
ラベルが持つすべてのcaseラベル要素がeと互換性がある場合、そのcaseラベルはeと互換性があります。
次の1つのみが当てはまる場合、定数のcaseラベル要素cは、eと互換性があります。
型e、Tが
enum型でない場合、char
、byte
、short
、int
、Character
、Byte
、Short
、Integer
またはString
である場合、cswitchブロックに関連付けられているすべてのはTと互換性がある代入です(5.2)。case
定数型e、Tがenum型である場合、c
switchブロックに関連付けられているすべてのは型Tのenum定数です。case
定数
型eが参照型である場合、
null
caseラベル要素はeと互換性があります。default
caseラベル要素は、常にeと互換性があります。パターンのcaseラベル要素pは、eがpと互換性がある場合、eと互換性があります(14.30.1)。
switch
文またはswitch
式のswitchブロックは、セレクタ式の型と互換性がある必要があり、そうでない場合、コンパイル時にエラーが発生します。
switch
式またはswitch
文に次の両方が当てはまる場合、コンパイル時にエラーになります。
switchブロックにデフォルトswitchラベルがあり、
パターンがセレクタ式の型の合計であるパターンのcaseラベル要素を持つswitchブロックにswitchラベルがあります(14.30.3)。
セレクタ式の合計であるパターンは、すべての値に一致するため、デフォルトswitchラベルのように動作します。
次のいずれかが保持される場合にかぎり、型Tは、Cという名前のsealed
クラスまたはインタフェースをサポートします。
TはCの名前を付けるクラス型であり、クラスCは
sealed
およびabstract
の両方です。TはCの名前を付けるインタフェース型であり、インタフェースCは
sealed
です。Tは型変数であり、その上限はCをサポートします。
Tは交差型T1
&
...&
Tnであり、型TiはCをサポートします(1 ≤ i ≤ n)。
次のいずれかが当てはまる場合、switch
式またはswitch
文のswitchブロックは、型Tのセレクタ式について網羅的です。
TはenumクラスにEの名前を付け、Eのすべてのenum定数は定数switchラベル要素としてswitchブロックに出現します。
デフォルトswitchラベルは許可されますが、すべてのenum定数がswitchラベルに出現する場合は必須ではないことに注意してください。次に例を示します。
enum E { F, G, H } static int testEnumCoverage(E e) { return switch(e) { case F -> 0; case G -> 1; case H -> 2; // No default required! }; }
Tは、Cという名前の
sealed
クラスまたはインタフェースをサポートし、switchブロックは、型Tで適用可能なCの許容された直接サブクラスおよびサブインタフェースすべてについて網羅的です。次のいずれかが保持される場合、Cという名前のクラスまたはインタフェースは型Uで適用可能です。
Cは汎用クラスまたはインタフェースではなく、UからCへのキャスト変換(5.5)があります。
Cは、型パラメータF1...Fnを持つ汎用クラスまたはインタフェースであり、Uから型C<X1, ..., Xn>へのキャスト変換があります(X1, ..., Xnは、Cの本体でスコープ内にない異なる型引数です)。
デフォルトswitchラベルは許可されますが、
sealed
クラスまたはインタフェースの許容された直接サブクラスおよびサブインタフェースすべてについてswitchブロックが網羅的である場合、必須ではありません。次に例を示します。sealed interface I permits A, B, C {} final class A implements I {} final class B implements I {} record C(int j) implements I {} // Implicitly final static int testExhaustive1(I i) { return switch(i) { case A a -> 0; case B b -> 1; case C c -> 2; // No default required! }; }
許容された直接サブクラスまたはサブインタフェースは、汎用
sealed
スーパークラスまたはスーパーインタフェースの特定のインスタンス化のみを拡張できるという事実は、switchブロックが網羅的であるかどうかを考慮するときにそれが適用可能でない場合があることを意味します。次に例を示します。sealed interface J<X> permits D, E {} final class D implements J<String> {} final class E<X> implements J<X> {} static int testExhaustive2(J<Integer> ji) { return switch(ji) { // Exhaustive! case E<Integer> e -> 42; }; }
このセレクタ式には型
J<Integer>
があるため、許容された直接サブクラスD
は適用可能ではありません。ji
の値がD
のインスタンスである可能性があるためです。したがって、switchブロックは、唯一の適用可能な許容された直接サブクラスE
を対象とするため網羅的です。switchブロックのswitchラベルに、パターンpがTの合計であるパターンのcaseラベル要素pがあります(14.30.3)。
switchブロックにデフォルトswitchラベルがあります。
switch
文または式は、そのswitchブロックがセレクタ式の型について網羅的である場合、網羅的です。
いくつかのパターンの意味は照合先の式の型によって決定されるため、switchラベルに出現するパターンは解決される必要があります(14.30.2)。
型Tでswitchブロックを解決すると、同じswitchブロックになります。ただし、すべてのswitchラベルLが型TでのLの解決の結果に置き換えられる場合を除きます。
型TでのswitchラベルLの解決は、それが使用しているすべてのdefault
およびcase
ラベルを次のように解決することによって進められます。
Lが
default
ラベルを使用している場合、解決されたswitchラベルはdefault
ラベルを使用します。Lが
case
ラベルを使用している場合、解決されたswitchラベルは、次のように決定されたcaseラベル要素を持つcase
ラベルを使用します。Lに出現する定数のcaseラベル要素がすべて、解決されたswitchラベルに出現します。
Lに出現する
default
caseラベル要素がすべて、解決されたswitchラベルに出現します。Lに
null
caseラベル要素が出現し、パターンのcase要素ラベルが出現しない場合は、解決されたswitchラベルにnull
caseラベル要素が出現します。Lに
null
caseラベル要素が出現しない場合は、Lに出現するパターンのcaseラベル要素pすべてについて、解決されたswitchラベルにパターンのcase要素ラベルqが出現します(qは、型Tでのパターンpの解決の結果です)。型でのパターンの解決の定義は、14.30.2に示されています。
Lに
null
caseラベル要素が出現する場合は、Lに出現するパターンのcaseラベル要素のうち、型Uのパターン変数xを宣言する型パターンであるものすべてについて、解決されたswitchラベルにパターンのcase要素qが出現します(qは、型Uのxを宣言するパターンです)。null
case要素ラベルも含むswitchラベルに出現するパターンのcase要素ラベルはすべて、型パターンである必要があります(14.11.1)。この最後のルールは、switchラベルの解決の重要な側面に対処します。switchラベルに
null
と型パターンの両方のcaseラベル要素がある場合、これらは、任意のパターンである単一のパターンのcaseラベル要素に解決されます。これにより、照合される値がnull
である場合でも、型パターンによって宣言されたパターン変数の初期化が常にパターン・マッチングのセマンティックによって処理されることが確実になります。
switch
文の実行(14.11.3)およびswitch
式の評価(15.28.2)の両方で、解決されたswitchブロックのswitchラベルがセレクタ式の値と一致するに適用されるかどうかを決定する必要があります。解決されたswitchブロックのswitchラベルが指定された値と一致するに適用されるかどうかを決定するにはの決定は、値がswitchブロックに関連付けられている次のとおりです。case
定数と比較され、次のようになります。
値がnull参照の場合、値に適用されるswitchブロックの最初の(ある場合) switchラベルを次のように決定します。
null
caseラベル要素を持つswitchラベルが適用されます。値がpと一致する場合、パターンのcaseラベル要素pを持つswitchラベルが適用されます(14.30.2)。
値がnull参照ではない場合、値に適用されるswitchブロックの最初の(ある場合) switchラベルを次のように決定します。
次の両方が当てはまる場合、定数のcaseラベル要素cを持つswitchラベルが適用されます。
値の型が
Character
、Byte
、Short
またはInteger
である場合、値は最初にボックス化解除変換の対象になります(5.1.8)。この変換が突然完了する場合、適用も同じ理由で突然完了すると言えます。この変換が正常に完了する場合、定数cがボックス化解除値と等しいときはswitchラベルが適用されます。case
定数の1つが値と等しい場合、case
定数を含むcase
ラベルが一致します。値の型が
Character
、Byte
、Short
またはInteger
でない場合、定数cが値と等しいときはswitchラベルが適用されます。
等価どちらの場合も、等価は、値がString
でないかぎり、==
演算子によって定義されます(15.21)。Stringの場合は、等価はクラスString
のequals
メソッドによって定義されます。値がパターンpと一致する場合、パターンのcaseラベル要素pを持つswitchラベルが適用されます(14.30.2)。パターン・マッチングが突然完了する場合、適用も同じ理由で突然完了します。
デフォルトswitchラベルではないswitch
ラベルがcase
一致しない適用されない
が、デフォルトswitchラベルがある場合、default
デフォルトswitchラベルが適用されます。default
switchブロックは最大で1つのデフォルトswitchラベルを持つことができます。
switchラベルは、いくつかのcase
定数のcaseラベル要素をcase
定数含む持つことができます。ラベルは、その定数のいずれかがセレクタ式の値と一致等しい場合、セレクタ式の値と一致しますに適用されます。たとえば、次のコードで、enum変数day
が表示されているenum定数のいずれかである場合、switchラベルは一致します。case
switch (day) { ... case SATURDAY, SUNDAY : System.out.println("It's the weekend!"); break; ... }
パターンのcaseラベル要素を持つswitchラベルが適用された場合、これは、パターンに対する値のパターン・マッチングのプロセスが成功したことによるものです(14.30.2)。値がパターンと正常に一致すると、パターン・マッチングのプロセスは、パターンによって宣言されたパターン変数を初期化します。
null
は、定数式ではないためcase
定数として使用できません。case
null
が許可されている場合でも、そのcase
内のコードを決して実行できないため、好ましくありません。つまり、セレクタ式が参照型(すなわち、String
またはボックス化プリミティブ型またはenum型)である場合、セレクタ式が実行時にnull
に評価されると例外が発生します。Javaプログラミング言語の設計者の判断では、case
ラベル一致がない、またはdefault
ラベル一致がないより、例外を伝播するほうがよい結果になります。
CおよびC++で、
switch
文の本体は文(単数および複数)にすることができ、case
ラベルをその文で直接含む必要はありません。単純なループを考えてみましょう。for (i = 0; i < n; ++i) foo();
ここで
n
は正であると知られています。ダフスデバイスとして知られているトリックは、ループをアンロールするためにCまたはC++で使用できますが、これはJavaプログラミング言語では有効なコードではありません。int q = (n+7)/8; switch (n%8) { case 0: do { foo(); // Great C hack, Tom, case 7: foo(); // but it's not valid here. case 6: foo(); case 5: foo(); case 4: foo(); case 3: foo(); case 2: foo(); case 1: foo(); } while (--q > 0); }
幸いにも、このトリックは広く認識または使用されてはいないようです。さらに、今日ではあまり必要ではなくなりました。この種のコード変換は、適切に最新式の最適化コンパイラの領域にあります。
14.11.2 switch
文のSwitchブロック
switchブロック(14.11.1)の一般的なルールに加えて、switch
文のswitchブロックにはさらにルールがあります。
拡張switch
文は、(i)セレクタ式の型がchar
、byte
、short
、int
、Character
、Byte
、Short
、Integer
、String
またはenum型ではないか、(ii)少なくとも1つのswitchラベルがパターンのcaseラベル要素またはnull
caseラベル要素を持つかのいずれかです。
つまり、すべてのswitch
文のswitchブロックに対して次のすべてが当てはまる必要があり、そうでない場合はコンパイル時にエラーが発生します。
1つのみのdefault
ラベルがswitch
ブロックに関連付けられています。switchブロックのすべてのswitchルール式は、文の式です(14.8)。
switch
文は、switchブロックの矢印(->
)の右にどの式が出現するか、つまり、どの式をswitchルール式として使用できるかという観点で、switch
式と異なります。switch
文では、文の式のみをswitchルール式として使用できますが、switch
式では、任意の式を使用できます(15.28.1)。switch
文が拡張switch
文である場合、それは網羅的である必要があります。
Java SE 17より前は、
switch
文(およびswitch
式)は2通りに制限されていました: (i)セレクタ式の型は、整数型、enum型またはString
に制限されていました。(ii)定数のcaseラベル要素のみがサポートされていました。さらに、switch
式とは異なり、switch
文は網羅的である必要はありませんでした。これは多くの場合、バグの検出を困難にする原因になり、switchラベルは適用されず、switch
文は何もしません。次に例を示します。enum E { A, B, C} E e = ...; switch (e) { case A -> System.out.println("A"); case B -> System.out.println("B"); // No case for C! }
Java SE 17では、前述の2つの制限を取り除くという意味で
switch
文が拡張されました。Javaプログラミング言語の設計者が、拡張switch
文はswitch
式と揃える必要があり、網羅的である必要があると決めました。これは多くの場合、自明なデフォルトswitchラベルの追加で達成されます。たとえば、次の拡張switch
文は網羅的ではありません。Object o = ...; switch (o) { // Error - non-exhaustive switch! case String s -> System.out.println("A string!"); }
しかし、それは簡単に網羅的にすることができます。
Object o = ...; switch (o) { case String s -> System.out.println("A string!"); default -> {} }
互換性の理由で、拡張
switch
文ではないswitch
文は網羅的である必要はありません。網羅性を必要とすることの結果の1つとして、拡張
switch
文は、通常のswitch
文とは異なり、空のswitchブロックを持つことができません。
14.11.3 switch
文の実行
switch
文の実行は常に、解決されたswitchブロックに関連します(14.11.1)。switchブロックは型Tで解決され、ここでTはセレクタ式の型です。
switch
文は、最初にセレクタ式を評価することで実行されます。次に、
セレクタ式の評価が突然完了する場合、
switch
文全体が同じ理由で突然完了します。それ以外の場合、セレクタ式の評価の結果が
null
であり、解決されたswitchブロックのswitchラベルが適用されないときは、NullPointerException
がスローされてswitch
文全体がその理由で突然完了します。適用が突然完了する場合、switch
文全体が同じ理由で突然完了します。
- それ以外の場合、セレクタ式の評価の結果が型
Character
、Byte
、Short
またはInteger
のとき、ボックス化解除変換の対象になります(5.1.8)。この変換が突然完了する場合、switch
文全体が同じ理由で突然完了します。
セレクタ式の評価が正常に完了する場合、結果が非null
で後続のボックス化解除変換(ある場合)が正常に完了するとき、switch
文の実行は、解決されたswitchブロックに関連付けられた内のswitchラベルがセレクタ式の値と一致するに適用されるかどうかを決定することで続行されます(14.11.1)。次に、
適用が突然完了する場合、
switch
文全体が同じ理由で突然完了します。一致する適用されるswitchラベルがない場合、switch
文が拡張switch
文のときは、IncompatibleClassChangeError
がスローされ、switch
文全体がその理由で突然完了します。それ以外の場合、switch
文全体が正常に完了します。switchラベルが
一致する適用される場合、次のいずれかが適用保持されます。switchルール式のswitchラベルである場合、switchルール式は必然的に文の式です(14.11.2)。文の式が評価されます。評価が正常に完了する場合、
switch
文は正常に完了します。評価の結果が値の場合、それは破棄されます。switchルール・ブロックのswitchラベルである場合、ブロックが実行されます。このブロックが正常に完了する場合、
switch
文は正常に完了します。switchルール
throw
文のswitchラベルである場合、throw
文が実行されます。switchラベルが付いた文グループのswitchラベルである場合、switchラベルに続くswitchブロックのすべての文が順番に実行されます。これらの文が正常に完了する場合、
switch
文は正常に完了します。それ以外の場合、
一致する適用されるswitchラベルに続く文がswitchブロック内になく、switch
文が正常に完了します。
switchブロックの文または式の実行が突然完了する場合、次のように処理されます。
ラベルなしの
break
のために文の実行が突然完了する場合、それ以上のアクションは行われず、switch
文は正常に完了します。ラベルありの
break
による突然の完了は、ラベルが付いた文の一般的なルールによって処理されます(14.7)。文または式の実行が他の理由のために突然完了する場合、
switch
文は同じ理由で突然完了します。yield
文による突然の完了は、switch式の一般的なルールによって処理されます(15.28.2)。
例14.11.3-1.switch
文のフォールスルー
セレクタ式がswitchラベルに一致しswitchラベルが適用され、そのswitchラベルがswitchルールに対するものである場合、switchラベルによって導入されたswitchルール式または文が実行されるのみです。文グループ用のswitchラベルの場合、そのswitchラベルに続くswitchブロックのすべてのブロック文が、後続のswitchラベルの後に出現するものを含めて実行されます。その効果は、CおよびC++でのように、文の実行が「ラベルをフォールスルー」できることです。
たとえば、次のプログラムの場合:
class TooMany {
static void howMany(int k) {
switch (k) {
case 1: System.out.print("one ");
case 2: System.out.print("too ");
case 3: System.out.println("many");
}
}
public static void main(String[] args) {
howMany(3);
howMany(2);
howMany(1);
}
}
各case
のコードが次のcase
のコードにフォールスルーするswitch
ブロックを含みます。結果として、プログラムの出力は次のようになります。
many
too many
one too many
フォールスルーは判断しにくいバグの原因になる場合があります。コードがこのようにcase
からcase
にフォールスルーする予定ではない場合、break
文を使用して、いつ制御を転送する必要があるかを示すことができます。または、switchルールを次のプログラムのように使用できます。
class TwoMany {
static void howMany(int k) {
switch (k) {
case 1: System.out.println("one");
break; // exit the switch
case 2: System.out.println("two");
break; // exit the switch
case 3: System.out.println("many");
break; // not needed, but good style
}
}
static void howManyAgain(int k) {
switch (k) {
case 1 -> System.out.println("one");
case 2 -> System.out.println("two");
case 3 -> System.out.println("many");
}
}
public static void main(String[] args) {
howMany(1);
howMany(2);
howMany(3);
howManyAgain(1);
howManyAgain(2);
howManyAgain(3);
}
}
このプログラムは次を出力します。
one
two
many
one
two
many
14.30 パターン
パターンは、値に対して実行できるテストを示します。パターンは文および式のオペランドとして出現し、テストする値を提供します。パターンはパターン変数として知られるローカル変数を宣言します。
パターンに対して値をテストするプロセスはパターン・マッチングと呼ばれます。値がパターンに正常に一致する場合、パターン・マッチングのプロセスが、パターンによって宣言されたパターン変数を初期化します。
パターン変数は、パターン・マッチングが成功するスコープ(6.3)内にのみあるため、パターン変数が初期化されています。初期化されていないパターン変数を使用することはできません。
14.30.1 パターンの種類
プライマリ・パターンはパターンの最も単純な形であり、そこから他のすべてが構成されます。型パターンは、値がパターンに出現する型のインスタンスであるかどうかをテストするために使用されます。カッコ付きのパターンは、構文的にプライマリ・パターンとしても扱われます。
ガード付きパターンは、含まれたプライマリ・パターンおよび含まれたガーディング式で構成され、値がパターンと一致するかどうか、さらに、ガーディング・ブール式がtrue
であるかどうかをテストするために使用されます。
- Pattern:
- TypePattern
- Pattern:
- PrimaryPattern
- GuardedPattern
- GuardedPattern:
- PrimaryPattern
&&
ConditionalAndExpression - PrimaryPattern:
- TypePattern
(
Pattern)
- TypePattern:
- LocalVariableDeclaration
- LocalVariableDeclaration:
- {VariableModifier} LocalVariableType VariableDeclaratorList
- VariableModifier:
- Annotation
final
- LocalVariableType:
- UnannType
var
- VariableDeclaratorList:
- VariableDeclarator {
,
VariableDeclarator}- VariableDeclarator:
- VariableDeclaratorId [
=
VariableInitializer]- VariableDeclaratorId:
- Identifier [Dims]
- Dims:
- {Annotation}
[
]
{{Annotation}[
]
}
UnannTypeについては、8.3を参照してください。
特殊なanyパターンもあります。これは、パターンの解決のプロセスから発生するパターンです(14.30.2)。
現在、anyパターンは、パターン
instanceof
式、またはswitch
式またはswitch
文のパターン・ラベルには出現できません。Javaプログラミング言語の将来のバージョンで、この制限が緩和される可能性があります。
型パターンは、パターン変数として知られる1つのローカル変数を宣言します。ローカル変数宣言の識別子は、パターン変数の名前を指定します。
型パターンで宣言されたローカル変数のルールについては、14.4に規定されています。さらに、次のすべてが当てはまる必要があり、そうでない場合、コンパイル時にエラーが発生します。
LocalVariableTypeは参照型を示します(また、
var
ではありません)。VariableDeclaratorListは単一のVariableDeclaratorで構成されています。
VariableDeclaratorにはイニシャライザがありません。
VariableDeclaratorIdにカッコのペアがありません。
パターン変数の型は、LocalVariableTypeによって示された参照型です。
型パターンの型は、そのパターン変数の型です。
カッコ付きのパターンは、含まれたパターンによって宣言されたローカル変数を宣言します。
カッコ付きのパターンの型は、含まれたパターンの型です。
anyパターンは、パターン変数として知られる1つのローカル変数を宣言します。
anyパターンのパターン変数は、参照型である型を持ちます。
anyパターンの型は、そのパターン変数の型です。
次のいずれかが当てはまる場合、パターン変数はガード・パターンp&&e
によって宣言されます。
パターン変数は、パターン
p
によって宣言されます当てはまる場合、パターン変数は
e
によって導入されます(6.3.1)。
パターン変数のいずれかが、含まれたパターンによって宣言され、当てはまる場合にガード付きパターンのガーディング式によっても宣言される場合、コンパイル時にエラーになります。
使用されているが、ガード付きパターンのガーディング式で宣言されていない変数は、final
または事実上のfinal (4.12.4)のいずれかである必要があります。
ガード付きのパターンの型は、含まれたパターンの型です。
式eは、eがTとダウンキャスト互換性がある場合、型Tのパターンと互換性があります(5.5)。
式eは、次のいずれかが当てはまる場合、パターンと互換性があります。
- パターンはanyパターンです。または
- パターンは型Tであり、eはTとダウンキャスト互換性があります(5.5)。
式のパターンとの互換性は、
instanceof
パターン一致演算子(15.20.2)、およびswitchラベル内のパターンを使用しているswitch
式またはswitch
文(14.11.1)によって使用されます。
14.30.2 パターン・マッチング
パターン・マッチングは、実行時にパターンに対して値をテストするプロセスです。パターン・マッチングは、文実行(14.1)および式評価(15.1)とは異なります。値がパターンに正常に一致する場合、パターン・マッチングのプロセスが、パターンによって宣言されたパターン変数を初期化します。
パターン・マッチングが実行される前に、パターンはまず、照合される式の型に関して解決され、修正された可能性があるパターンになります。
型Uでのパターンの解決のルールは、次のとおりです。
Uの合計である、含まれたパターンpがあるガード付きパターンは、型Uでのpの解決の結果に解決されます。Uの合計ではない、含まれたパターンpおよびガーディング式eがあるガード付きパターンは、含まれたパターンが型Uでのpの解決の結果であり、ガーディング式がeであるガード付きパターンに解決されます。
Uの合計である、型Tのパターン変数xを宣言する型パターンpは、型Tのxを宣言するanyパターンに解決されます。それ以外の場合、pに解決されます。
含まれたパターンpがあるカッコ付きのパターンは、含まれたパターンが型Uでのpの解決の結果であるカッコ付きのパターンに解決されます。
anyパターンpは、pに解決されます。
パターンの解決のこのプロセスは、型パターンに対してnull参照値を照合したときに発生する問題に対処します。値が型Uの型パターンと一致するかどうかのチェックには、値をUにキャストできるかどうかの(実行時の)チェックが含まれます。しかし、null参照はチェックなしで任意の参照型にキャストできます。それでも、型Vの式が最終的にnull参照に評価される場合、Vが型パターンの型のサブタイプであった場合のみ照合が成功すると期待します。
これは、パターン・マッチングがコンパイル時型を含む必要があることを示します。コンパイル時型に関してパターン・マッチングの実行時プロセスを直接定義するのではなく、パターン・マッチングが次のプロパティを満たす必要があります。静的型がTである任意の式の値は常に、TがSのサブタイプである型Sの型パターンと一致します。
このように、コンパイル時に、パターン・マッチングされる式の(コンパイル時)型に関してパターンを解決します。パターンが常に一致するとコンパイル時に判断できる場合、それは、特別なanyパターン(実行時型の調査なしで、定義によってすべての値が一致します)に変換または解決されます。これにより、照合する値がnull参照である場合に、期待した動作が確実に行われます。その他の場合、パターンの解決によってパターンがそのままの状態になります。
一部のパターンには、パターン・マッチング中に評価される式が含まれます。含まれている式の評価が突然完了する場合、パターン・マッチングは突然完了すると言い表されます。突然の完了には常に理由が関連付けられており、それは常に、指定された値を持つthrow
です。パターン・マッチングは、突然完了しない場合、正常に完了すると言い表されます。
値がパターンと一致するかどうかを決定するルールおよびパターン変数を初期化するルールは、次のとおりです。
値v (null参照を含む)はanyパターンと一致します。
anyパターンによって宣言されるパターン変数は、vに初期化されます。
null参照は、型パターンと一致しません。
この場合、型パターンによって宣言されたパターン変数は初期化されません。
null参照ではない値vは、
ClassCastException
を発生させずにvをTにキャストできる場合、型Tの型パターンと一致します。そうでない場合、一致しません。vが一致する場合、型パターンによって宣言されたパターン変数はvに初期化されます。
vが一致しない場合、型パターンによって宣言されたパターン変数は初期化されません。
null参照である値を対象とするルールはありません。これは、パターン・マッチングを実行する単独のコンストラクトである
instanceof
パターン一致演算子(15.20.2)が、値がnull参照ではない場合にのみパターン・マッチングを実行するためです。Javaプログラミング言語の将来のバージョンで、他の式および文でのパターン・マッチングが許可される可能性があります。
値は、(i)含まれているパターンと一致し、(ii)ガーディング式が
true
に評価される場合、ガード付きパターンと一致します。それ以外のときは一致しません(すべてのステップが正常に完了した場合)。値を含まれているパターンと照合するパターンが突然完了する場合、または値が含まれているパターンと一致しない場合、ガーディング式を評価するための試行は行われません。どちらかのステップが突然完了する場合、パターン・マッチングは同じ理由で突然完了します。含まれているパターンと一致する値のプロセスは、ガーディング式などで使用できるパターン変数を初期化できることに注意してください。
if (o instanceof String s && s.length() > 2) { System.out.println("A string containing at least two characters"); }
- 値は、含まれているパターンと一致する場合、カッコ付きのパターンと一致します。そうでない場合、一致しません。値を含まれているパターンと照合するパターンが突然完了する場合、パターン・マッチングは同じ理由で突然完了します。
14.30.3 パターン全域性および優位性
次のように、パターンは型Tの合計であると言い表されます。
ガード付きパターンは、(i)含まれているパターンがTの合計であり、(ii)ガーディング式が値
true
の定数式である場合、Tの合計です。型Sの型パターンは、TのイレイジャがSのイレイジャのサブタイプである場合、Tの合計です。
カッコ付きのパターンは、含まれているパターンがTの合計である場合、Tの合計です。
anyパターンは、任意のTの合計です。
qと一致するすべての値がpとも一致する場合、パターンpはパターンqより優先されると言い表され、次のように定義されています。
pがTの合計である場合、パターンpは型Tの型パターンより優先されます。
pがqより優先される場合、パターンpは、含まれたパターンqがあるガード付きパターンより優先されます。
pがqより優先される場合、パターンpは、含まれたパターンqがあるカッコ付きのパターンより優先されます。
pがTの合計である場合、パターンpは型Tのanyパターンより優先されます。
第15章: 式
15.20 関係演算子
15.20.2 instanceof
演算子
instanceof
式は型比較またはパターン・マッチングのいずれかを実行できます。
- InstanceofExpression:
- RelationalExpression
instanceof
ReferenceType - RelationalExpression
instanceof
PatternPrimaryPattern
instanceof
キーワードの右側のオペランドがReferenceTypeである場合、instanceof
キーワードは型比較演算子です。
instanceof
キーワードの右側のオペランドがPattern PrimaryPatternである場合、instanceof
キーワードはパターン一致演算子です。
次のルールは、instanceof
が型比較演算子の場合に適用されます。
式RelationalExpressionの型は、参照型またはnull型である必要があり、そうでない場合、コンパイル時にエラーが発生します。
RelationalExpressionは、ReferenceTypeとダウンキャスト互換性がある必要があり(5.5)、そうでない場合、コンパイル時にエラーが発生します。
実行時に型比較演算子の結果は次のように決定されます。
RelationalExpressionの値がnull参照(4.1)の場合、結果は
false
になります。RelationalExpressionの値がnull参照ではない場合、
ClassCastException
を発生させずに値をReferenceTypeにキャストできるときは、結果はtrue
です。そうでない場合、false
です。
次のルールは、instanceof
がパターン一致演算子の場合に適用されます。
式RelationalExpressionの型は、参照型またはnull型である必要があり、そうでない場合、コンパイル時にエラーが発生します。
RelationalExpressionは、
PatternPrimaryPattern (14.30.1)と互換性がある必要があり、そうでない場合、コンパイル時にエラーが発生します。RelationalExpressionの型がPattern,の型のサブタイプである場合、コンパイル時にエラーが発生します。PrimaryPatternが式RelationalExpressionの型の合計である場合(14.30.3)、コンパイル時にエラーが発生します。
この要件により、要領を得ないパターン・マッチング
instanceof
式を排除します。String s = ... if (s instanceof Object o) // Compile-time error! System.out.println("Everything is an object!");
実行時に、パターン一致演算子の結果は次のように決定されます(pは、型がRelationalExpressionのパターンの解決の結果のパターンです(14.30.2))。
RelationalExpressionの値がnull参照の場合、結果は
false
になります。RelationalExpressionの値がnull参照ではない場合、値が
パターン解決されたパターンpと一致する場合、結果はtrue
です(14.30.2)。そうでない場合、false
です。解決されたパターンpと値を照合するパターンが突然完了する場合、instanceof
式は同じ理由で突然完了します。true
結果の悪影響は、パターン解決されたパターンpで宣言されたパターン変数が初期化されることです。
例15.20.2-1.型比較演算子
class Point { int x, y; }
class Element { int atomicNumber; }
class Test {
public static void main(String[] args) {
Point p = new Point();
Element e = new Element();
if (e instanceof Point) { // compile-time error
System.out.println("I get your point!");
p = (Point)e; // compile-time error
}
}
}
このプログラムの結果、コンパイル時に2つのエラーが発生します。Element
または使用可能なそのサブクラス(ここには示されていません)のインスタンスがPoint
のサブクラスのインスタンスではない可能性があるため、キャスト(Point)e
は正しくありません。まったく同じ理由により、instanceof
式も正しくありません。一方、クラスPoint
がElement
のサブクラスであった場合(この例では明らかに奇妙な表記です):
class Point extends Element { int x, y; }
キャストは可能になりますが、実行時のチェックが必要になるため、instanceof
式が実用的かつ有効です。キャスト(Point)e
は、e
の値を型Point
に正確にキャストできない場合には実行されないため、これによって例外が呼び出されることはありません。
Java SE 16より前は、reifiableにするには型比較演算子のReferenceTypeオペランドが必要でした(4.7)。これにより、そのすべての型引数がワイルドカードでないかぎり、パラメータ化型を使用できませんでした。多くのパラメータ化型の使用を許可するために、その要件はJava SE 16で取り除かれました。たとえば、次のプログラムで、静的型List<Integer>
のあるメソッド・パラメータx
に、さらに詳細なパラメータ化型ArrayList<Integer>
があるかどうかを実行時にテストすることは有効です。
import java.util.ArrayList;
import java.util.List;
class Test2 {
public static void main(String[] args) {
List<Integer> x = new ArrayList<Integer>();
if (x instanceof ArrayList<Integer>) { // OK
System.out.println("ArrayList of Integers");
}
if (x instanceof ArrayList<String>) { // error
System.out.println("ArrayList of Strings");
}
if (x instanceof ArrayList<Object>) { // error
System.out.println("ArrayList of Objects");
}
}
}
最初のinstanceof
式は、List<Integer>
からArrayList<Integer>
へのキャスト変換があるため有効です。ただし、2番目および3番目のinstanceof
式では、List<Integer>
からArrayList<String>
またはArrayList<Object>
へのキャスティング変換がないため、どちらもコンパイル時にエラーが発生します。
15.28 switch
式
switch
式は、式の値に応じて、いくつかの文または式の1つに制御を転送します。その式のすべての可能な値が処理される必要があり、いくつかの文または式のすべてがswitch
式の結果の値を生成する必要があります。
- SwitchExpression:
switch
(
Expression)
SwitchBlock
Expressionはセレクタ式と呼ばれます。セレクタ式の型はchar
、byte
、short
、int
、Character
、Byte
、Short
、Integer
、String
またはenum型(8.9)である必要があり、そうでない場合、コンパイル時にエラーが発生します。
switch
式およびswitch
文(14.11)の両方の本体は、switchブロックと呼ばれます。switch
式またはswitch
文のどちらに出現するかに関係なく、すべてのswitchブロックに適用される一般的なルールは、14.11.1に示されています。便宜上、ここでは14.11.1の次のプロダクションを示します。
- SwitchBlock:
{
SwitchRule {SwitchRule}}
{
{SwitchBlockStatementGroup} {SwitchLabel:
}}
- SwitchRule:
- SwitchLabel
->
Expression;
- SwitchLabel
->
Block- SwitchLabel
->
ThrowStatement- SwitchBlockStatementGroup:
- SwitchLabel
:
{SwitchLabelBlockStatements:
}
- SwitchLabel:
case
CaseConstant {,
CaseConstant}default
- SwitchLabel:
- CaseOrDefaultLabel {
:
CaseOrDefaultLabel }- CaseOrDefaultLabel:
case
CaseLabelElement {,
CaseLabelElement }default
- CaseLabelElement:
- CaseConstant
- Pattern
null
default
- CaseConstant:
- ConditionalExpression
15.28.1 switch
式のSwitchブロック
switchブロック(14.11.1)の一般的なルールに加えて、switch
式のswitchブロックにはさらにルールがあります。つまり、switch
式のswitchブロックに対して次のすべてが当てはまる必要があり、そうでない場合はコンパイル時にエラーが発生します。
セレクタ式の型がenum型ではない場合、switchブロックに関連付けられた
default
ラベルが1つのみあります。セレクタ式の型がenum型の場合、(i) switchブロックに関連付けられた
case
定数のセットが、enum型のすべてのenum定数を含み、(ii)最大で1つのdefault
ラベルがswitchブロックに関連付けられます。default
ラベルは許可されますが、case
ラベルがすべてのenum定数を対象とする場合、必須ではありません。
switchブロックがswitchルールで構成される場合、switchルール・ブロックは正常に完了できません(14.22)。
switchブロックがswitchラベルが付いた文グループで構成される場合、switchブロックの最後の文は正常に完了できず、switchブロックの最後のswitchラベルが付いた文グループの後にはswitchラベルがありません。
switch
式は、switch
文とは異なり、空のswitchブロックを持つことができません。さらに、switch
式は、switchブロックの矢印(->
)の右にどの式が出現するか、つまり、どの式をswitchルール式として使用できるかという観点で、switch
文と異なります。switch
式では、任意の式をswitchルール式として使用できますが、switch
文では、文の式のみを使用できます(14.11.1)。
switch
式は網羅的である必要があり(14.11.1)、そうでない場合、コンパイル時にエラーが発生します。
switch
式の結果式は、次のように決定されます。
switchブロックがswitchルールで構成される場合、各switchルールが順番に考慮されます。
switchルールが
...
->
式の形式の場合、式はswitch
式の結果式です。switchルールが
...
->
ブロックの形式の場合、yieldターゲットが指定されたswitch
式であるブロックのyield
文に直接含まれているすべての式は、switch
式の結果式です。
switchブロックがswitchラベルが付いた文グループで構成される場合、yieldターゲットが指定された
switch
式であるswitchブロックのyield
文に直接含まれるすべての式がswitch
式の結果式です。
switch
式に結果式がない場合、コンパイル時にエラーになります。
switch
式が割当てコンテキストまたは呼出しコンテキスト(5.2、5.3)に出現する場合、それは複合式です。それ以外の場合はスタンドアロン式です。
複合switch
式がターゲット型Tの特定の種類のコンテキストに出現する場合、その結果式も同様にターゲット型Tの同じ種類のコンテキストに出現します。
複合switch
式は、その結果式のそれぞれがTと互換性がある場合、ターゲット型Tと互換性があります。
複合switch
式の型は、そのターゲット型と同じです。
スタンドアロンswitch
式の型は、次のように決定されます。
結果式がすべて同じ型を持つ場合(null型も可能(4.1))、それは
switch
式の型です。それ以外の場合、各結果式の型が
boolean
またはBoolean
のときは、ボックス化解除変換(5.1.8)が型Boolean
の各結果式に適用され、switch
式は型boolean
を持ちます。それ以外の場合、各結果式の型が数値型(5.1.8)に変換可能なときは、
switch
式の型は、結果式に適用された一般的な数値プロモーション(5.6)の結果です。それ以外の場合、ボックス化変換(5.1.7)がプリミティブ型を持つ各結果式に適用され、その後、
switch
式の型は、結果式の型の最小上限(4.10.4)への取得変換(5.1.10)の適用の結果になります。
15.28.2 switch
式の実行時評価
switch
式の評価は常に、解決されたswitchブロックに関連します(14.11.1)。switchブロックは型Tで解決され、ここでTはセレクタ式の型です。
switch
式は、最初にセレクタ式を評価することで評価されます。次に、
セレクタ式の評価が突然完了する場合、
switch
式が同じ理由で突然完了します。それ以外の場合、セレクタ式の評価の結果が
null
であり、解決されたswitchブロックのswitchラベルが適用されないときは、NullPointerException
がスローされてswitch
式全体がその理由で突然完了します。適用が突然完了する場合、switch
式全体が同じ理由で突然完了します。
- それ以外の場合、セレクタ式の評価の結果が非
null
で、型Character
、Byte
、Short
またはInteger
のとき、ボックス化解除変換の対象になります(5.1.8)。この変換が突然完了する場合、switch
式全体が同じ理由で突然完了します。
セレクタ式の評価が正常に完了する場合、結果が非null
で後続のボックス化解除変換(ある場合)が正常に完了するとき、switch
式の評価は、解決されたswitchブロックに関連付けられた内のswitchラベルがセレクタ式の値と一致するに適用されるかどうかを決定することで続行されます(14.11.1)。次に、
適用が突然完了する場合、
switch
式全体が同じ理由で突然完了します。switchラベルが
一致しない適用されない場合、IncompatibleClassChangeError
がスローされ、switch
式全体がその理由で突然終了します。switchラベルが
一致する適用される場合、次のいずれかが適用保持されます。switchルール式のswitchラベルである場合、式が評価されます。評価の結果が値である場合、
switch
式は同じ値で正常に完了します。switchルール・ブロックのswitchラベルである場合、ブロックが実行されます。このブロックが正常に完了する場合、
switch
式は正常に完了します。switchルール
throw
文のswitchラベルである場合、throw
文が実行されます。それ以外の場合、switchブロック内の適用される
一致するswitchラベルの後のすべての文が順番に実行されます。これらの文が正常に完了する場合、switch
式は正常に完了します。
switchブロックの文または式の実行が突然完了する場合、次のように処理されます。
式の実行が突然完了する場合、
switch
式が同じ理由で突然完了します。値がVの
yield
のために文の実行が突然完了する場合、switch
式は正常に完了し、switch
式の値はVです。文の実行が値のある
yield
以外の理由のために突然完了する場合、switch
文は同じ理由で突然完了します。
第16章: 明確な割当て
16.2 明確な割当ておよび文
16.2.9 switch
文
Vは、次のすべてが当てはまる場合のみ、
switch
文(14.11)の後で割当て[解除]されます。Vは、
switch
文を終了できるすべてのbreak
文(14.15)の前で割当て[解除]されます。switchブロックの各switchルール(14.11.1)に対して、Vは、switchルール式、switchルール・ブロック、またはswitchルールによって導入されたswitchルール
throw
文の後で割当て[解除]されます。switchブロックにswitchラベルが付いた文グループがある場合、Vは、最後のswitchラベルが付いた文グループの最後のブロック文の後で割当て[解除]されます。
switchブロックにswitchブロックがセレクタ式の型を対象とする場合、またはdefault
ラベルがない}
セパレータが続くswitchラベルでswitchブロックが終了する場合、Vはセレクタ式の後で割当て[解除]されます。
Vが
switch
文の前で割当て[解除]される場合のみ、Vは、switch
文のセレクタ式の前で割当て[解除]されます。Vが
switch
文のセレクタ式の後で割当て[解除]される場合のみ、Vは、switchルール式、switchルール・ブロック、またはswitchブロックのswitchルールによって導入されたswitchルールthrow
文の前で割当て[解除]されます。次の両方が当てはまる場合のみ、Vは、switchブロックのswitchラベルが付いた文グループの最初のブロック文の前で割当て[解除]されます。
Vは、
switch
文のセレクタ式の後で割当て[解除]されます。switchラベルが付いた文グループがswitchブロックで初めてではない場合、Vは、前述のswitchラベルが付いた文グループの最後のブロック文の後で割当て[解除]されます。
Vが前述のブロック文の後で割当て[解除]される場合のみ、Vは、switchブロックで最初のswitchラベルが付いた文グループではないブロック文の前で割当て[解除]されます。