このドキュメントでは、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:
caseCaseConstant {,CaseConstant}default
- SwitchLabel:
- CaseOrDefaultLabel {
:CaseOrDefaultLabel } - CaseOrDefaultLabel:
caseCaseLabelElement {,CaseLabelElement }default- CaseLabelElement:
- CaseConstant
- Pattern
nulldefault
- 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ラベル要素、
defaultcaseラベル要素、またはnullcaseラベル要素のいずれかである必要があります。switchラベルでは、複数の
defaultラベルを使用できません。switchラベルは、複数の
defaultcaseラベル要素を持つことができません。switchラベルは、
defaultcaseラベル要素を持ち、かつdefaultラベルを使用することはできません。switchラベルは、複数の
nullcaseラベル要素を持つことができません。switchラベルに
nullcaseラベル要素があり、switchラベルにパターンのcase要素ラベルもある場合、それらは型パターン(14.30.1)である必要があります。switchラベルは、複数のパターンのcaseラベル要素を持つことができません。
switchラベルは、パターンのcaseラベル要素を持ち、かつ
defaultラベルを使用することはできません。switchラベルは、パターンのcaseラベル要素および
defaultcaseラベル要素の両方を持つことはできません。
これらのルールは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ラベルは、nullcaseラベル要素を持つ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が参照型である場合、
nullcaseラベル要素はeと互換性があります。defaultcaseラベル要素は、常に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に出現する
defaultcaseラベル要素がすべて、解決されたswitchラベルに出現します。Lに
nullcaseラベル要素が出現し、パターンのcase要素ラベルが出現しない場合は、解決されたswitchラベルにnullcaseラベル要素が出現します。Lに
nullcaseラベル要素が出現しない場合は、Lに出現するパターンのcaseラベル要素pすべてについて、解決されたswitchラベルにパターンのcase要素ラベルqが出現します(qは、型Tでのパターンpの解決の結果です)。型でのパターンの解決の定義は、14.30.2に示されています。
Lに
nullcaseラベル要素が出現する場合は、Lに出現するパターンのcaseラベル要素のうち、型Uのパターン変数xを宣言する型パターンであるものすべてについて、解決されたswitchラベルにパターンのcase要素qが出現します(qは、型Uのxを宣言するパターンです)。nullcase要素ラベルも含む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ラベルを次のように決定します。
nullcaseラベル要素を持つ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ラベルが適用されます。defaultswitchブロックは最大で1つのデフォルトswitchラベルを持つことができます。
switchラベルは、いくつかのcase定数のcaseラベル要素をcase定数含む持つことができます。ラベルは、その定数のいずれかがセレクタ式の値と一致等しい場合、セレクタ式の値と一致しますに適用されます。たとえば、次のコードで、enum変数dayが表示されているenum定数のいずれかである場合、switchラベルは一致します。caseswitch (day) { ... case SATURDAY, SUNDAY : System.out.println("It's the weekend!"); break; ... }パターンのcaseラベル要素を持つswitchラベルが適用された場合、これは、パターンに対する値のパターン・マッチングのプロセスが成功したことによるものです(14.30.2)。値がパターンと正常に一致すると、パターン・マッチングのプロセスは、パターンによって宣言されたパターン変数を初期化します。
nullは、定数式ではないためcase定数として使用できません。casenullが許可されている場合でも、その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
instanceofReferenceType - RelationalExpression
instanceofPatternPrimaryPattern
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:
caseCaseConstant {,CaseConstant}default
- SwitchLabel:
- CaseOrDefaultLabel {
:CaseOrDefaultLabel }- CaseOrDefaultLabel:
caseCaseLabelElement {,CaseLabelElement }default- CaseLabelElement:
- CaseConstant
- Pattern
nulldefault
- 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ラベルが付いた文グループではないブロック文の前で割当て[解除]されます。