このドキュメントでは、switchのパターン・マッチングとレコード・パターン(どちらもJava SE 19のプレビュー機能)をサポートするためにJava言語仕様に加えられた変更について説明します。この機能の概要は、それぞれJEP 427およびJEP 405を参照してください。
変更は、JLSの既存のセクションについて説明しています。新しいテキストはこのように示され、削除されたテキストはこのように示されます。必要に応じて、説明と考察が端の方にグレーのボックスで囲まれて記載されています。
変更ログ:
2022-06-01: - 14.11.1.2 実行時にnull caseラベルが解決され判断される方法の取扱いを修正しました。- 14.30.2 anyパターンに対するパターン・マッチングのセマンティクスを修正しました。
2022-05-17: 15.20.2 パターンの解決に関して欠落していたテキストを追加しました(これについては、14.30.2で明確に説明されていました)。
2022-05-10: 14.30.2 レコード・アクセサが突然完了することで生じるMatchExceptionについて、この理由を原因として明示的に記録する例が必要です。
2022-04-26: JEP 427に更新しました(switchのパターン・マッチング(第3プレビュー))。
2022-04-25: 一部のコード例を修正して、「洗練されていない」パターン・ラベルを「ガードなし」に名前変更しました。default switchラベルの周囲のテキストを明確化しました。
2022-04-20: amber-spec-expertsメーリング・リストに対するフィードバックに従った修正を含む2番目の草稿。
2022-04-07: 初稿をリリースしました。様々な不具合の修正に加えて、JEP 420プレビューからの主な変更点は次のとおりです。
第3章: 字句構造
3.9 キーワード
ASCII文字で構成された51個の文字シーケンスは、キーワードとして使用するために予約されており、識別子(3.8)として使用することはできません。その他の同じくASCII文字で構成された16個の文字シーケンスは、出現するコンテキストによっては、キーワードや別のトークンとして解釈される可能性があります。
- Keyword:
- ReservedKeyword
- ContextualKeyword
- ReservedKeyword:
- (次のいずれか)
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:
- (次のいずれか)
exports permits to with
module provides transitive yield
non-sealed record uses
open requires var
opens sealedwhen
キーワード
constおよびgotoは、現在使用されていませんが、予約されています。これにより、これらのC++のキーワードがプログラムで誤って出現した場合にJavaコンパイラがより適切なエラー・メッセージを作成できるようになります。
キーワード
strictfpは廃止されています。新しいコードには使用しないでください。
キーワード
_(アンダースコア)は、将来パラメータ宣言で使用できるように予約されています。
trueおよびfalseはキーワードではなく、booleanリテラルです(3.10.3)。
nullはキーワードではなく、nullリテラルです(3.10.8)。
入力文字から入力要素への還元(3.5)の際に、コンテキスト・キーワードと概念的に一致する入力文字のシーケンスは、次の両方の条件が満たされる場合にのみ、コンテキスト・キーワードに還元されます。
シーケンスは、次のように、構文文法(2.3)の適切なコンテキストで指定された終端として認識されます。
moduleおよびopenについては、ModuleDeclaration (7.7)で終端として認識される場合。exports、opens、provides、requires、to、usesおよびwithについては、ModuleDirectiveで終端として認識される場合。transitiveについては、RequiresModifierで終端として認識される場合。たとえば、シーケンス
requirestransitive;の認識では、RequiresModifierが使用されていないため、このtransitiveという用語はコンテキスト・キーワードではなく識別子に還元されます。varについては、LocalVariableType (14.4)またはLambdaParameterType (15.27.1)で終端として認識される場合。その他のコンテキストでは、識別子として
varを使用しようとすると、varはTypeIdentifier (3.8)ではないためにエラーが発生します。yieldについては、YieldStatement (14.21)で終端として認識される場合。その他のコンテキストでは、識別子として
yieldを使用しようとすると、yieldはTypeIdentifierとUnqualifiedMethodIdentifierのどちらでもないためにエラーが発生します。recordについては、RecordDeclaration (8.10)で終端として認識される場合。non-sealed、permitsおよびsealedについては、NormalClassDeclaration (8.1)またはNormalInterfaceDeclaration (9.1)で終端として認識される場合。whenについては、SwitchBlock (14.11.1)で終端として認識される場合。
シーケンスの直前または直後に、JavaLetterOrDigitと一致する入力文字がない。
一般に、ソースコードで誤って空白を省略すると、「可能なかぎり長い変換」ルール(3.2)によって、入力文字のシーケンスが識別子としてトークン化されます。たとえば、12文字の入力文字シーケンス
p u b l i c s t a t i cは、予約キーワードのpublicおよびstaticとしてではなく、常に識別子のpublicstaticとしてトークン化されます。2つのトークンを使用する場合は、それらを空白またはコメントで区切る必要があります。
前述のルールは、「可能なかぎり長い変換」ルールと連動して、コンテキスト・キーワードが現れるコンテキスト内で直感的な結果を生成します。たとえば、11文字の入力文字シーケンス
v a r f i l e n a m eは、通常、識別子varfilenameとしてトークン化されますが、ローカル変数宣言では、最初の3文字の入力文字は、前述のルールの最初の条件によって暫定的にコンテキスト・キーワードvarとして認識されます。ただし、シーケンス内の空白の不足を見落とすと、その次の8文字の入力文字を識別子filenameとして認識することによる混乱が生じます。(これは、シーケンスが異なるコンテキストで異なるトークン化を経ることを意味します。ほとんどのコンテキストでは識別子ですが、ローカル変数宣言ではコンテキスト・キーワードと識別子です)。したがって、2番目の条件により、直後の入力文字のfがJavaLetterOrDigitであるという理由で、コンテキスト・キーワードvarの認識を防止します。そのため、シーケンスv a r f i l e n a m eは、ローカル変数宣言の識別子varfilenameとしてトークン化されます。
コンテキスト・キーワードの慎重な認識の別の例として、15文字の入力文字のシーケンス
n o n - s e a l e d c l a s sについて考えてみます。このシーケンスは、通常、識別子non、演算子-および識別子sealedclassの3つのトークンに変換されますが、最初の条件が成立する通常のクラス宣言では、最初の10の入力文字は暫定的にコンテキスト・キーワードnon-sealedとして認識されます。シーケンスを3つの非キーワード・トークンではなく2つのキーワード・トークン(non-sealedとclass)に解釈されることを回避し、classの前の空白を省略するプログラマに報酬を与えないようにするために、2番目の条件によってコンテキスト・キーワードの認識を防止します。そのため、シーケンスn o n - s e a l e d c l a s sは、クラス宣言では3つのトークンとしてトークン化されます。
前述のルールでは、最初の条件は構文文法の詳細に依存しますが、Javaプログラミング言語のコンパイラは、入力プログラムを完全に解析することなくルールを実装できます。たとえば、コンテキスト・キーワードの有効な使用がキーワードとしてトークン化され、識別子の有効な使用が識別子としてトークン化されることがヒューリスティックで保証されているかぎり、ヒューリスティックを使用してトークナイザのコンテキスト状態を追跡できます。また、コンパイラでは常にコンテキスト・キーワードを識別子としてトークン化し、そうした識別子の特別な使用の認識は、それより後のフェーズに任せることもできます。
第5章: 変換およびコンテキスト
5.5 キャスト・コンテキスト
キャスト・コンテキストを使用すると、キャスト式(15.16)のオペランドは、キャスト演算子によって明示的に指定された型に変換されるようになります。割当てコンテキストおよび呼出しコンテキストとは対照的に、キャスト・コンテキストでは、5.1で定義されている変換をさらに多用できるとともに、これらの変換の組合せをより多く使用できます。
式がプリミティブ型の場合は、キャスト・コンテキストに次のいずれかを使用できます。
恒等変換(5.1.1)
拡張プリミティブ変換(5.1.2)
縮小プリミティブ変換(5.1.3)
拡張および縮小プリミティブ変換(5.1.4)
ボックス化変換(5.1.7)
ボックス化変換後の拡張参照変換(5.1.5)
式が参照型の場合は、キャスト・コンテキストに次のいずれかを使用できます。
恒等変換(5.1.1)
拡張参照変換(5.1.5)
拡張参照変換後のボックス化解除変換
拡張参照変換後のボックス化解除変換に続く拡張プリミティブ変換
縮小参照変換(5.1.6)
縮小参照変換後のボックス化解除変換
ボックス化解除変換(5.1.8)
ボックス化解除変換後の拡張プリミティブ変換
式にNULL型がある場合は、式は任意の参照型にキャストされることがあります。
キャスト・コンテキストで、チェックされているか部分的にチェックされていない縮小参照変換(5.1.6.2、5.1.6.3)が使用されると、式の値のクラスに対して実行時チェックが実行され、ClassCastExceptionが発生する可能性があります。それ以外の場合は、実行時チェックは実行されません。
チェックされていない縮小参照変換以外のキャスト変換によって式を参照型に変換できる場合、その式(またはその値)は、参照型とのダウンキャスト互換性があると表現されます。
参照型Sの式が、別の参照型Tとのダウンキャスト互換性がある場合、型Sは型Tにダウンキャスト変換可能であると表現します。
次の表に、特定のキャスト・コンテキストで使用される変換を列挙します。それぞれの変換は、次の記号で示されます。
- は、変換が許可されていないことを示します
≈は、恒等変換(5.1.1)を示します
ωは、拡張プリミティブ変換を示します(5.1.2)
ηは、縮小プリミティブ変換を示します(5.1.3)
ωηは、拡張および縮小プリミティブ変換を示します(5.1.4)
⇑は、拡張参照変換を示します(5.1.5)
⇓は、縮小参照変換を示します(5.1.6)
⊕は、ボックス化変換を示します(5.1.7)
⊗は、ボックス化解除変換を示します(5.1.8)
この表では、記号間のカンマは、キャスト・コンテキストがある変換とそれに続く別の変換を使用することを示します。型Objectは、8つのラッパー・クラスBoolean、Byte、Short、Character、Integer、Long、Float、Double以外の任意の参照型を意味します。
表5.5-A.プリミティブ型へのキャスト
| キャスト先→ キャスト元↓ |
byte |
short |
char |
int |
long |
float |
double |
boolean |
|---|---|---|---|---|---|---|---|---|
byte |
≈ | ω | ωη | ω | ω | ω | ω | - |
short |
η | ≈ | η | ω | ω | ω | ω | - |
char |
η | η | ≈ | ω | ω | ω | ω | - |
int |
η | η | η | ≈ | ω | ω | ω | - |
long |
η | η | η | η | ≈ | ω | ω | - |
float |
η | η | η | η | η | ≈ | ω | - |
double |
η | η | η | η | η | η | ≈ | - |
boolean |
- | - | - | - | - | - | - | ≈ |
Byte |
⊗ | ⊗,ω | - | ⊗,ω | ⊗,ω | ⊗,ω | ⊗,ω | - |
Short |
- | ⊗ | - | ⊗,ω | ⊗,ω | ⊗,ω | ⊗,ω | - |
Character |
- | - | ⊗ | ⊗,ω | ⊗,ω | ⊗,ω | ⊗,ω | - |
Integer |
- | - | - | ⊗ | ⊗,ω | ⊗,ω | ⊗,ω | - |
Long |
- | - | - | - | ⊗ | ⊗,ω | ⊗,ω | - |
Float |
- | - | - | - | - | ⊗ | ⊗,ω | - |
Double |
- | - | - | - | - | - | ⊗ | - |
Boolean |
- | - | - | - | - | - | - | ⊗ |
Object |
⇓,⊗ | ⇓,⊗ | ⇓,⊗ | ⇓,⊗ | ⇓,⊗ | ⇓,⊗ | ⇓,⊗ | ⇓,⊗ |
表5.5-B.参照型へのキャスト
| キャスト先→ キャスト元↓ |
Byte |
Short |
Character |
Integer |
Long |
Float |
Double |
Boolean |
Object |
|---|---|---|---|---|---|---|---|---|---|
byte |
⊕ | - | - | - | - | - | - | - | ⊕,⇑ |
short |
- | ⊕ | - | - | - | - | - | - | ⊕,⇑ |
char |
- | - | ⊕ | - | - | - | - | - | ⊕,⇑ |
int |
- | - | - | ⊕ | - | - | - | - | ⊕,⇑ |
long |
- | - | - | - | ⊕ | - | - | - | ⊕,⇑ |
float |
- | - | - | - | - | ⊕ | - | - | ⊕,⇑ |
double |
- | - | - | - | - | - | ⊕ | - | ⊕,⇑ |
boolean |
- | - | - | - | - | - | - | ⊕ | ⊕,⇑ |
Byte |
≈ | - | - | - | - | - | - | - | ⇑ |
Short |
- | ≈ | - | - | - | - | - | - | ⇑ |
Character |
- | - | ≈ | - | - | - | - | - | ⇑ |
Integer |
- | - | - | ≈ | - | - | - | - | ⇑ |
Long |
- | - | - | - | ≈ | - | - | - | ⇑ |
Float |
- | - | - | - | - | ≈ | - | - | ⇑ |
Double |
- | - | - | - | - | - | ≈ | - | ⇑ |
Boolean |
- | - | - | - | - | - | - | ≈ | ⇑ |
Object |
⇓ | ⇓ | ⇓ | ⇓ | ⇓ | ⇓ | ⇓ | ⇓ | ≈ |
例5.5-1.参照型のキャスト
class Point { int x, y; }
interface Colorable { void setColor(int color); }
class ColoredPoint extends Point implements Colorable {
int color;
public void setColor(int color) { this.color = color; }
}
final class EndPoint extends Point {}
class Test {
public static void main(String[] args) {
Point p = new Point();
ColoredPoint cp = new ColoredPoint();
Colorable c;
// The following may cause errors at run time because
// we cannot be sure they will succeed; this possibility
// is suggested by the casts:
cp = (ColoredPoint)p; // p might not reference an
// object which is a ColoredPoint
// or a subclass of ColoredPoint
c = (Colorable)p; // p might not be Colorable
// The following are incorrect at compile time because
// they can never succeed as explained in the text:
Long l = (Long)p; // compile-time error #1
EndPoint e = new EndPoint();
c = (Colorable)e; // compile-time error #2
}
}
ここで、最初のコンパイル時エラーが発生する理由は、クラス型のLongとPointが関連していない(これらは同じではなく、どちらも他方のサブクラスではない)ためです。これらの間のキャストは常に失敗します。
2つ目のコンパイル時エラーは、EndPoint型の変数が、インタフェースColorableを実装する値を参照できないために発生します。これは、EndPointはfinal型であり、final型の変数は常にコンパイル時の型と同じ実行時の型の値を保持するためです。そのため、変数eの実行時の型は正確にEndPoint型になっている必要があり、EndPoint型はColorableを実装しません。
例5.5-2.配列型のキャスト
class Point {
int x, y;
Point(int x, int y) { this.x = x; this.y = y; }
public String toString() { return "("+x+","+y+")"; }
}
interface Colorable { void setColor(int color); }
class ColoredPoint extends Point implements Colorable {
int color;
ColoredPoint(int x, int y, int color) {
super(x, y); setColor(color);
}
public void setColor(int color) { this.color = color; }
public String toString() {
return super.toString() + "@" + color;
}
}
class Test {
public static void main(String[] args) {
Point[] pa = new ColoredPoint[4];
pa[0] = new ColoredPoint(2, 2, 12);
pa[1] = new ColoredPoint(4, 5, 24);
ColoredPoint[] cpa = (ColoredPoint[])pa;
System.out.print("cpa: {");
for (int i = 0; i < cpa.length; i++)
System.out.print((i == 0 ? " " : ", ") + cpa[i]);
System.out.println(" }");
}
}
このプログラムはエラーなしでコンパイルされ、次の出力を生成します。
cpa: { (2,2)@12, (4,5)@24, null, null }
例5.5-3.実行時の互換性のない型のキャスト
class Point { int x, y; }
interface Colorable { void setColor(int color); }
class ColoredPoint extends Point implements Colorable {
int color;
public void setColor(int color) { this.color = color; }
}
class Test {
public static void main(String[] args) {
Point[] pa = new Point[100];
// The following line will throw a ClassCastException:
ColoredPoint[] cpa = (ColoredPoint[])pa;
System.out.println(cpa[0]);
int[] shortvec = new int[2];
Object o = shortvec;
// The following line will throw a ClassCastException:
Colorable c = (Colorable)o;
c.setColor(0);
}
}
このプログラムはコンパイルにキャストを使用しますが、型に互換性がないため、実行時に例外をスローします。
第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ラベルによって導入されるanyパターン変数が、関連付けられた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ラベルによって導入されるanyパターン変数が、関連付けられた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に適用されます。
pのレコード・コンポーネント・パターン・リスト内のパターンqごとに、qで宣言されたパターン変数は、リスト内でその後に続くすべてのレコード・パターン・コンポーネントで確実に照合されます。
qで宣言されたパターン変数が、すでにリスト内で後続のレコード・パターン・コンポーネントのスコープ内にあると、コンパイル時エラーになります。
このルールは、パターン変数は単一のレコード・コンポーネント・パターン・リストで最大1回しか宣言できないという線形性制約を強制します。2つのレコード・コンポーネントの値が等しくなるように指定することは、パターンで直接エンコードできませんが、後続のコードで処理する必要があります。
Object o = ... if (o instanceof Point(int x, int y) && (x == y)) { // Not the pattern Point(int x, int x)! System.out.println("Point on the diagonal"); }レコード・パターンpが名前付きレコード・パターンである場合は、レコード・パターン変数が、すでにリスト内で後続のレコード・パターン・コンポーネントのスコープ内にあると、コンパイル時エラーになります。
6.3.4 Switchラベルのパターン変数のスコープ
パターン変数は、switchラベルおよびパターンに関連付けられた任意のwhen式でサポートされるパターンによって導入でき、関連付けられたswitch式(6.3.1.6)またはswitch文(6.3.2.6)の関連部分のスコープ内にあります。
次の各ルールは、switchラベルに適用されます。
パターン変数は、pによって宣言される場合、パターンpをサポートするswitchラベルによって導入されます。
switchラベルでサポートされるパターンによって宣言されたパターン変数は、関連するすべての
when式で確実に一致します。switchラベルでサポートされるパターンによって宣言されたanyパターン変数が、関連付けられた
when式でスコープ内にすでにある場合、コンパイル時エラーになります。パターン変数は、関連付けられた
when式eを持つパターンがtrueのときにeよって導入された場合、そのパターンをサポートするswitchラベルによって導入されます(6.3.1)。
第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の実行は、コンパイル時に認識されなかった許容されている直接サブクラスのインスタンスが検出された場合に、エラーで失敗する(MatchExceptionがスローされる)ことがあります(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)の実行はリンケージ・エラーで失敗する(MatchExceptionがスローされる)ことがあります(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:
caseCaseElement {,CaseElement }default- CaseElement:
- CaseConstant
- Pattern [ Guard ]
nulldefault
- CaseConstant:
- ConditionalExpression
- Guard:
whenExpression
switchブロックは次のいずれかで構成できます。
Switchルール。これは、
->を使用して、switchルール式、switchルール・ブロックまたはswitchルールthrow文を導入します。またはSwitchラベルが付いた文グループ。これは
:を使用してswitchラベルが付いたブロック文を導入します。
すべてのswitchルールおよびswitchラベルの付いた文グループは、switchラベル (caseラベルまたはdefaultラベルのいずれか)で始まります。複数のswitchラベルが、1つのswitchラベルが付いた文グループに許可されます。
caseラベルには、1つ以上のcase定数があります。すべてのcase定数は、定数式(15.29)またはenum定数の名前(8.9.1)のいずれかである必要があり、そうでない場合、コンパイル時にエラーが発生します。
Switchラベルおよびそのcase定数は、switchブロックに関連付けられていると言い表されます。switchブロックに関連付けられている2つのcase定数が同じ値を持つことはなく、そうでない場合、コンパイル時にエラーが発生します。
すべてのswitchルールおよびswitchラベル付き文グループは、switchラベルで始まります。switchラベルは、1つ以上のcaseラベルおよびdefaultラベルで構成されます。caseラベルには、1つ以上のcaseラベル要素があります。switchラベルは、case要素を含むcaseラベルがある場合、case要素をサポートすると表現します。
パターン・ケース要素には、when式が含まれていることがあります。パターンcase要素は、(i)その要素にwhen式が関連付けられていない場合、または(ii)値がtrueの定数式であるwhen式がその要素に関連付けられている(15.29)場合に、ガードなしと表現されます。case要素は、パターンcase要素でない場合や、またはガードなしパターンcase要素である場合にはガードされません。
switchブロックのすべてのswitchラベルについて、次のすべてが当てはまる必要があり、そうでない場合、コンパイル時にエラーが発生します。
switchラベルでサポートされるすべての
case定数は、定数式(15.29)またはenum定数の名前(8.9.1)のいずれかです。switchラベルでは、同じ値の複数の
case定数をサポートしません。switchラベルでは、
case定数とパターンの両方をサポートしません。switchラベルでは、複数の
defaultをサポートしません。switchラベルでは、
defaultサポートしません。また、defaultラベルは付けられません。switchラベルは、複数の
defaultラベルを使用できません。switchラベルでは、複数の
nullをサポートしません。switchラベルでは、複数のパターンをサポートしません。
switchラベルでは、パターンと
defaultの両方をサポートしません。switchラベルでは、パターンと
defaultラベルの両方をサポートしません。switchラベルでは、レコード・パターンと
nullの両方をサポートしません。
これらのルールはswitchラベルの形式を制限します。複雑さの多くは、文グループのswitchラベル内でcaseラベル要素を結合する2つの方法が歴史的にサポートされていることが原因です(たとえば、
case 1: case 2とcase 1,2)。
パターンのcase要素に関連付けられたwhen式には、boolean型またはBoolean型が必要です。when式で使用されているが宣言されていない変数は、finalであるかeffectively finalであることが必要です(4.12.4)。when式がfalseの値を持つ定数式(15.29)の場合は、コンパイル時エラーになります。
switchルールのswitchラベルが複数のcaseまたはdefaultラベルで構成されている場合、コンパイル時にエラーになります。
つまり、
case 1: case 2 -> ...は有効なswitchルールではありませんが、case 1, 2 -> ...と書くことができます。
switchラベルは、defaultラベルがある場合や、default case要素をサポートする場合に、default switchラベルであると表現されます。switchブロックに複数のdefault switchラベルがある場合、コンパイル時エラーになります。
次の両方に該当する場合、switch文またはswitch式のswitchブロックは、セレクタ式T型との互換性があります。
Tがenum型でない場合は、switchブロックに関連付けられているすべての
case定数にTとの代入互換性がある(5.2)。- Tがenum型の場合は、switchブロックに関連付けられているすべての
case定数がT型のenum定数である。
switch文またはswitch式のswitchブロックは、switchブロック内のすべてのswitchラベルがTとのswitch互換性がある場合、セレクタ式T型とのswitch互換性があります。switchラベルは、そのラベルがサポートするすべてのcase要素(存在する場合)が次のように定義されているTとのswitch互換性がある場合、Tとのswitch互換性があります。
case定数cはT型とのswitch互換性があります。このTは、cにTとの代入互換性がある場合(5.2)、char、byte、short、int、Character、Byte、Short、IntegerまたはStringのいずれかになります。case定数cは、cがT型のenum定数である場合、enum型Tとのswitch互換性があります。パターンcase要素pは、pがT型で適用可能な場合(14.30.3)、Tとのswitch互換性があります。
nullcase要素は、Tが参照タイプの場合、Tとのswitch互換性があります。defaultcase要素は、すべての型とのswitch互換性があります。
特に、
defaultラベルのみを含むswitchブロックは、すべてのセレクタ式との互換性があります。
switch文またはswitch式のswitchブロックは、セレクタ式の型とのswitch互換性が必要であり、そうでない場合はコンパイル時にエラーが発生します。
switchラベルは、前者が後者に適用されるすべての値に適用される場合、別のswitchラベルより優先されると表現します。switchブロック内のswitchラベルが、それに続くswitchラベルより優先される場合は、コンパイル時にエラーが発生します。優先を決定するルールは次のとおりです。
case定数をサポートするswitchラベルは、同じcase定数をサポートする別のswitchラベルより優先されます。これにより、次に示すように、2番目に現れる
case定数2が決して一致しない例が除外されます。int i = ...; switch (i) { case 1, 2 -> System.out.println("1 or 2"); case 2, 3 -> System.out.println("2 or 3"); // Error! }ガードなしパターンpをサポートするswitchラベルは、pがqより優先される場合、パターンqをサポートする別のswitchラベルよりも優先されます(14.30.3)。
when式でパターンをサポートするswitchラベルは、同じパターンをサポートする別のswitchラベルより優先されるとはみなされません。これにより、次の一般的なパターン・プログラミング・スタイルが可能になります。Object o = ...; switch (o) { case Integer i when i <= 0 -> ... case Integer i when i > 0 -> ... case Integer i -> ... ... }唯一の例外は、
when式の値がtrueの場合です。次に例を示します。Object o = ...; switch (o) { case Integer i when true -> ... // Allowed but ... why? case Integer i -> ... // Error - dominated switch label ... }ただし、
when式でパターンをサポートするswitchラベルは、パターンのみをサポートするswitchラベルよりも優先されます。たとえば、次はコンパイル時にエラーになります。Object o = ...; switch (o) { case List<?> l -> ... case List<?> l when l.size() == 2 -> ... // Error - dominated by previous switch label ... }パターンが別のパターンより優先される定義(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 }正確に言うと、優先は型のイレイジャに関して定義されます。たとえば、型パターン
ArrayList<? extends Number> alは、型パターンArrayList<Number> alnより優位であり、その逆も同様です。つまり、たとえば、次のswitchブロックはコンパイル時にエラーになります。List<Number> l = ...; switch (l) { case ArrayList<? extends Number> al -> ... case ArrayList<Number> aln -> ... // Error - dominated switch label ... }
パターンpをサポートするswitchラベルは、pが
case定数cよりも優先される場合、cをサポートする別のswitchラベルよりも優先されます。これは、次のように定義されます。型Tのパターン変数を宣言する型パターンは、プリミティブ型Pのラッパー・クラス(5.1.7)がTのイレイジャのサブタイプである場合、Pの定数cよりも優先されます。
T型のパターン変数を宣言する型パターンは、EがT型のイレイジャのサブタイプである場合、E型のenum定数cよりも優先されます。
カッコ付きパターンは、cよりも優先されるパターンが含まれている場合、定数cよりも優先されます。
最初のルールは、プリミティブ
case定数が優先されるcaseに対応します。Integer i = ...; switch (i) { case Integer j -> ... case 42 -> ... // Error - dominated! }2番目のルールは、enum
case定数が優先されるcaseに対応します。enum E { A, B, C } E value = ...; switch (value) { case E e -> ... case A -> ... // Error - dominated! }レコード・パターンについてのルールはありません。
when式の分析(通常は決定不能)は試行されません。たとえば、セレクタ式の値が42の場合、最初のswitchラベルが一致しない場合でも、次の結果はコンパイル時エラーになります。Integer i = ...; switch (i) { case Integer j when j != 42 -> ... case 42 -> ... // Error - dominated! ... }case定数をサポートするswitchラベルは、パターンをサポートするラベルの前に現れる必要があります。次に例を示します。Integer i = ...; switch (i) { case 42 -> ... case Integer j when j < 50 -> ... // All integers less than 50 // *except* 42 case Integer j -> ... // All other integers }
次の両方が当てはまるswitchラベルが付いた文グループで構成されるswitchブロックに文がある場合、コンパイル時にエラーになります。
パターン変数を導入する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文に次の両方が当てはまる場合、コンパイル時にエラーになります。
switchブロックにデフォルトswitchラベルがあり、
switchブロック内にパターンpをサポートするswitchラベルがあり、そのpはセレクタ式の型で無条件である(14.30.3)。
セレクタ式のタイプで無条件であるパターンは、その名前が示すとおり、すべての値に一致するため、default switchラベルのように動作します。
14.11.1.1 網羅的なSwitchブロック
switch式またはswitch文のswitchブロックは、(i) switchブロックにdefault switchラベルが含まれている場合、または(ii) switchラベルでサポートされているすべてのガードなしcase要素が含まれているセットがTに対して網羅的である(次のように定義されている)場合、T型のセレクタ式に対して網羅的です。
case要素のセットは、Eのすべてのenum定数が含まれている場合、enumクラス型Eに対して網羅的です。
デフォルトswitchラベルは許可されますが、すべてのenum定数がswitchラベルに出現する場合は必須ではないことに注意してください。次に例を示します。
enum E { F, G, H } static int testEnumExhaustive(E e) { return switch(e) { case F -> 0; case G -> 1; case H -> 2; // No default required! }; }型Tで無条件であるパターン(14.30.3)が含まれている場合、型Tに対するcase要素のセットは網羅的です。
パターンは、所定の型のすべての値と一致することがわかっている場合は、その型で無条件であると表現されます。たとえば、型パターン
Object oは、型Stringで無条件です。case要素のセットは、Tのすべての適用可能な許可された直接サブタイプに対して網羅的である場合、
abstractおよびsealedクラスまたはCという名前のインタフェースが含まれている型Tに対して網羅的です。型Tは、次のいずれかが成立する場合にのみ、Cという名前のインタフェースまたは
abstractおよびsealedクラスが含まれます。TはCの名前を付けるクラス型であり、クラスCは
sealedおよびabstractの両方です。TはCの名前を付けるインタフェース型であり、インタフェースCは
sealedです。Tは型変数であり、その上限はCを含んでいます。
Tは交差型T1
&...&Tnであり、型TiはC (1 ≤ i ≤ n)を含んでいます。
クラスまたはインタフェース型Tは、次のいずれかが成立する場合、
abstractおよびsealedクラスまたはインタフェースのCを指定するクラスまたはインタフェース型Sの適用可能な許可された直接サブタイプと表現されます。Tはクラスまたはインタフェース型Dであり、(i)DはCの許可された直接サブクラスまたはサブインタフェースであり、(ii) Dは汎用クラスまたはインタフェースではなく、(iii) SはDにダウンキャスト変換可能です(5.5)。
TはD<
?, ...,?>型(つまり、すべてがワイルドカードであるn個の型引数を持つDのパラメータ化)であり、(i) DはCの許可された直接サブクラスまたはサブインタフェースであり、(ii) Dはn個のパラメータを持つ汎用クラスまたはサブインタフェースであり、(iii) SはD<?, ...,?>にダウンキャスト可能です。
default switchラベルは許可されますが、
abstractおよび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! }; }switchブロックには、型
A、BおよびCの値と一致するパターンをサポートするスイッチ・ラベルが含まれていて、それ以外の型Iのインスタンスは許可されないため、このswitchブロックは網羅的です。許容された直接サブクラスまたはサブインタフェースは、汎用
sealedスーパークラスまたはスーパーインタフェースの特定のパラメータ化のみを拡張できるという事実は、switchブロックが網羅的であるかどうかを判断するときには考慮が不要な場合があることを意味します。次に例を示します。sealed interface J<X> permits D, E {} final class D<Y> 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>があり、jiの値がDのインスタンスである可能性はないため、許可された直接サブクラスDについて考慮する必要はありません。case要素のセットPは、(i) QがRという名前の型を持つレコード・パターンのみが含まれたPの空ではないサブセットであり、(ii) レコード・クラスRのレコード・コンポーネントが0個またはQが型Uでコンポーネントcから網羅的である場合に、レコード・クラスRを指定する型Tに対して網羅的です。このcはRのレコード・コンポーネント・リスト内の最初のコンポーネントであり、UはTの対応するコンポーネント・フィールドの型です。
レコード・クラスRを指定するレコード・タイプTがあるとすると、次の場合に、レコード・パターンPのセットは型Uでレコード・コンポーネントcから網羅的になります。- P内のすべてのレコード・パターンからのcに対応するコンポーネント・パターンを含むパターンのセットはUに対して網羅的であり、(ii) cがレコード・クラスRのレコード・コンポーネント・リストの最終コンポーネントではなく、次のいずれかが該当する場合。
セットQは、型Vでコンポーネントdから網羅的です。このdはcの後のコンポーネント、VはTの対応するコンポーネント・フィールドの型、QはU対して網羅的であるcに対応するコンポーネント・パターンを持つP内のすべてのレコード・パターンが含まれたパターンのセットです。
型Uは、
abstractおよびsealedクラスまたはインタフェースDを指定するクラスまたはインタフェース型で、すべての適用可能な許可された直接サブタイプUに対して、その型でセットPはコンポーネントcから網羅的です。
switch文または式は、そのswitchブロックがセレクタ式の型について網羅的である場合、網羅的です。
14.11.1.2 実行可能なSwitchブロックおよび実行時に適用されるSwitchラベルの決定
一部のパターンの意味は照合対象の式の型によって決まるため、パターン・マッチング(14.30.2)の実行前にパターンを解決しておく必要があります。そのため、switch式またはswitch文のswitchラベルでサポートされるanyパターンも解決する必要があります。
実行可能なswitch式またはswitch文は、次のように、セレクタ式Tの型に関してすべてのdefaultおよびcaseラベルが変換されているものです。
defaultラベルは、defaultラベルに変換されます。caseラベルLは、まず、すべてのcase要素が次のように変換されたcaseラベルに変換されます。case定数cは、case定数cに変換されます。defaultcase要素は、defaultcase要素に変換されます。nullcase要素は、nullcase要素に変換されます。パターンcase要素pは、パターンcase要素ラベルqに変換されます。このqは、型Tでパターンpを解決したときの結果です(14.30.2)。pに
when式が関連付けられている場合、qは同じ式に関連付けられます。
結果として変換された
caseラベルにnullcase要素とパターンcase要素pの両方がある場合(pは型Uのパターン変数xを宣言するパターン)、pは、型Uのxを宣言するanyパターン(14.30.1)に置き換えられます。前述したように、switchラベルがパターンと
nullの両方をサポートしている場合は、パターンをレコード・パターンにすることはできません(14.11.1)。この最後のステップでは、
switchのパターン・マッチングの特殊なケースに対応します。たとえば、ラベルcase null, String sはNULL参照値に適用され、さらに、この場合のパターン変数sはNULL参照値で初期化されます。これは、型パターンを型Stringのanyパターンに置き換えることで実現されます。
実行可能なswitch文の実行(14.11.3)と実行可能なswitch式の評価(15.28.2)の両方で、switchラベルがセレクタ式の値と一致するに適用されるかどうかを判断する必要があります。switchブロック内のswitchラベルが所定の値と一致するに適用されるかどうかを判断するために、の判断は、その値がswitchブロックに関連付けられたcase定数と比較されます次のように進められます。次のようになります。
値がnull参照の場合、値に適用されるswitchブロックの最初の(ある場合) switchラベルを次のように決定します。
nullをサポートするswitchラベルは、そのswitchラベルもサポートするすべてのパターン(ある場合)と値が一致する場合に適用されます。パターン・マッチングが突然完了した場合は、適用されるswitchラベルの判断についても同じ理由で突然完了します。具体的には、
nullをサポートしていてanyパターンをサポートしていないswitchラベルは、null参照値に適用されることを意味します。
値がnull参照ではない場合、値に適用されるswitchブロックの最初の(ある場合) switchラベルを次のように決定します。
case定数cをサポートするswitchラベルは、最初に値がボックス化解除変換(5.1.8)の対象になり、定数cがボックス化解除した値と等しくなる場合、Character、Byte、ShortまたはInteger型の値に適用されます。case定数の1つが値と等しい場合、case定数を含むcaseラベルが一致します。ボックス化解除変換は、ボックス化解除される値がNULL参照にならないことが保証されるため、正常に完了する必要があります。
等価性は、
==演算子で定義されます(15.21)。case定数cをサポートするswitchラベルは、Character、Byte、ShortまたはInteger型でない値に適用されます(その値と定数cが等しい場合)。等価性は
==演算子(15.21)によって定義されますが、値がStringの場合はStringクラスのequalsメソッドによって定義されます。パターンpをサポートするswitchラベルが値に適用されるかどうかの判断は、その値がパターンpと一致するかどうかをチェックする(14.30.2)ことから始まります。
パターン・マッチングが突然完了した場合は、適用されるswitchラベルを判断するプロセスについても同じ理由で突然完了します。
パターン・マッチングが成功して、パターンに
when式が関連付けられていない場合は、このswitchラベルが適用されます。パターン・マッチングが成功して、パターンに
when式が関連付けられている場合は、そのwhen式が評価されます。その結果がBoolean型の場合は、ボックス化解除変換(5.1.8)の対象になります。when式または後続のボックス化解除変換(ある場合)の評価がなんらかの理由で突然完了した場合、適用するswitchラベルを判断するプロセスは同じ理由で突然完了します。それ以外の場合は、その結果の値が
trueのときに、switchラベルが適用されます。一致する前述のルールがswitchブロック内のいずれのswitchラベルにも適用されない場合は、default switchラベルが適用されます。caseラベルがない場合は、defaultラベルが存在していれば、そのdefaultラベルが一致します。switchブロックは最大で1つのデフォルトswitchラベルを持つことができます。
switchラベルは、複数のcasecase定数を含めることがサポートできます。ラベルは、その定数のいずれかがセレクタ式の値と一致等しい場合、セレクタ式の値と一致しますに適用されます。たとえば、次のコードで、enum変数dayが表示されているenum定数のいずれかである場合、switchラベルは一致します。caseswitch (day) { ... case SATURDAY, SUNDAY : System.out.println("It's the weekend!"); break; ... }パターンをサポートする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) switchラベルの少なくとも1つがパターンまたはnullをサポートする文かのいずれかです。
つまり、すべてのswitch文のswitchブロックに対して次のすべてが当てはまる必要があり、そうでない場合はコンパイル時にエラーが発生します。
1つのみのdefaultラベルがswitchブロックに関連付けられています。switchブロックのすべてのswitchルール式は、文の式です(14.8)。
switch文は、switchブロックの矢印(->)の右にどの式が出現するか、つまり、どの式をswitchルール式として使用できるかという観点で、switch式と異なります。switch文では、文の式のみをswitchルール式として使用できますが、switch式では、任意の式を使用できます(15.28.1)。switch文が拡張switch文である場合、それは網羅的である必要があります。
Java SE 19より前の
switch文(およびswitch式)は、(i) セレクタ式の型が整数型(longを除く)、列挙型またはStringに制限されていて、(ii)case定数要素のみがサポートされていたという2つの制限がありました。さらに、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 19では、前述の2つの制限を取り除くという意味で
switch文が拡張されました。Javaプログラミング言語の設計者が、拡張switch文はswitch式と揃える必要があり、網羅的である必要があると決めました。多くの場合、これは自明的なdefaultラベルの追加によって達成されます。たとえば、次の拡張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文は網羅的である必要はありません。
14.11.3 switch文の実行
実行可能なswitch文(14.11.1.2)は、セレクタ式の最初の評価によって実行されます。その後、次のように処理されます。
セレクタ式の評価が突然完了する場合、
switch文全体が同じ理由で突然完了します。それ以外の場合、セレクタ式の評価の結果が
nullであり、解決されたswitchブロックのswitchラベルが適用されないときは、NullPointerExceptionがスローされてswitch文全体がその理由で突然完了します。適用するswitchラベルを決定するプロセスが突然完了する場合は、同じ理由でswitch文全体が突然完了します。
- それ以外の場合、セレクタ式の評価の結果が型
Character、Byte、ShortまたはIntegerのとき、ボックス化解除変換の対象になります(5.1.8)。この変換が突然完了する場合、switch文全体が同じ理由で突然完了します。
セレクタ式の評価が正常に完了する場合、結果が非nullで後続のボックス化解除変換(ある場合)が正常に完了するとき、switch文の実行は、解決されたswitchブロックに関連付けられた内のswitchラベルがセレクタ式の値と一致するに適用されるかどうかを決定することで続行されます(14.11.1.2)。その後、次のように処理されます。
適用するswitchラベルを決定するプロセスが突然完了する場合は、同じ理由で
switch文全体が突然完了します。switchラベルが
一致しない場合は、適用されない場合は、次のいずれかが成立します。switch文全体が正常に完了します。switch文が拡張switch文であり、セレクタ式の評価結果が列挙型の場合は、IncompatibleClassChangeErrorがスローされ、その理由からswitch文全体が突然完了します。switch文が拡張switch文であり、セレクタ式の評価結果が列挙型ではない場合は、MatchExceptionがスローされ、その理由からswitch文全体が突然完了します。switch文が拡張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 パターンの種類
型パターンは、値がパターンに出現する型のインスタンスであるかどうかをテストするために使用されます。パターンは、カッコで囲むと読みやすくなります。レコード・パターンは、値がレコード・クラス型のインスタンスであるかどうかをテストし、そうである場合は、レコード・コンポーネント値に対してパターン・マッチングを再帰的に実行するために使用されます。
- Pattern:
- TypePattern
- ParenthesizedPattern
- RecordPattern
- TypePattern:
- LocalVariableDeclaration
- ParenthesizedPattern:
(Pattern)- RecordPattern:
- ReferenceType RecordStructurePattern [ Identifier ]
- RecordStructurePattern:
([ RecordComponentPatternList ])- RecordComponentPatternList :
- Pattern {
,Pattern }
- 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を参照してください。
型パターンは、パターン変数として知られる1つのローカル変数を宣言します。ローカル変数宣言の識別子は、パターン変数の名前を指定します。
レコード・コンポーネント・パターン・リストに要素として現れない型パターンは、トップレベル型パターンと呼ばれています。
型パターンで宣言されたローカル変数のルールについては、14.4に規定されています。さらに、次のすべてが当てはまる必要があり、そうでない場合、コンパイル時にエラーが発生します。
トップレベル型パターンのLocalVariableTypeは、参照型(また、
varでないこと)を示します。VariableDeclaratorListは単一のVariableDeclaratorで構成されています。
VariableDeclaratorにはイニシャライザがありません。
VariableDeclaratorIdにカッコのペアがありません。
トップレベル型パターンで宣言されたパターン変数の宣言型は、LocalVariableTypeによって示される参照型です。
トップレベル型パターンではない型パターンで宣言されたパターン変数の宣言型は、次のように決定されます。
LocalVariableTypeがUnannTypeの場合、パターン変数の型はUnannTypeで示されます
LocalVariableTypeが
varの場合、パターン変数は、型Rのレコード・パターンのレコード・コンポーネント・パターン・リストに存在する必要があります。パターン変数の宣言型は、Rの対応するコンポーネント・フィールドの型です。次のレコード宣言について考えてみます。
record R<T>(ArrayList<T> r){}パターン
R<String>(var r)の場合、パターン変数rの宣言型はArrayList<String>です。
型パターンの型は、そのパターン変数の型です。
カッコ付きのパターンは、含まれたパターンによって宣言されたローカル変数を宣言します。
レコード・パターンは、ReferenceType、レコード・コンポーネント・パターン・リストおよびオプションの識別子で構成されます。ReferenceTypeがレコード・クラス・タイプ(8.10)でない場合、コンパイル時エラーが発生します。
識別子を持つレコード・パターンは、名前付きレコード・パターンと呼ばれます。
レコード・コンポーネント・パターン・リストの長さは、ReferenceTypeで指定されたレコード・クラスの宣言内のレコード・コンポーネント・リストと同じ長さである必要があります。そうでない場合は、コンパイル時エラーが発生します。
現時点では、可変個引数レコード・パターンはサポートされていません。これは、Javaプログラミング言語の将来のバージョンでサポートされる可能性があります。
レコード・パターンでは、レコード・コンポーネント・パターン・リスト内のパターンで宣言されているローカル変数(ある場合)を宣言します。
名前付きレコード・パターンでは、レコード・パターン変数と呼ばれるローカル変数も宣言します。この変数は識別子と呼ばれ、その宣言型はReferenceTypeで示される型です。
特殊なanyパターンもあります。これは、パターンの解決のプロセスから発生するパターンです(14.30.2)。
現時点では、anyパターンの構文は存在しないため、パターン
instanceof式のパターンとして使用することも、switch式またはswitch文のswitchラベルのパターンとして使用することもできません。Javaプログラミング言語の将来のバージョンで、この制限が緩和される可能性があります。
anyパターンは、パターン変数として知られる1つのローカル変数を宣言します。
anyパターンのパターン変数は、参照型の型を持ちます。
14.30.2 パターン・マッチング
パターン・マッチングは、実行時にパターンに対して値をテストするプロセスです。パターン・マッチングは、文実行(14.1)および式評価(15.1)とは異なります。値がパターンに正常に一致する場合、パターン・マッチング・プロセスが、パターンによって宣言されたすべてのパターン変数(ある場合)を初期化します。
パターン・マッチングの実行前に、すべてのパターンが、まず、照合される式のタイプ(switch文のセレクタ式またはswitch文、あるいはinstanceof式のRelationalExpressionのいずれか)に関して解決されます。その結果として、パターンが修正される可能性があります。
型Uでパターンを解決するには次のように指定します。
型Tのパターン変数xを宣言する型パターンpは、pがUで無条件の場合、型Tのxを宣言するanyパターンに解決されます。それ以外の場合は、pに解決されます。
含まれたパターンpがあるカッコ付きのパターンは、含まれたパターンが型Uでのpの解決の結果であるカッコ付きのパターンに解決されます。
- 型Rおよびレコード・コンポーネント・パターン・リストLのレコード・パターンpは、型Rのレコードパターンと、Rの対応するコンポーネント・フィールドの型でLのパターン(ある場合)ごとに順に解決した結果のレコード・コンポーネント・パターン・リストに解決されます。
このパターンを解決するプロセスは、レコード・パターンの正しいセマンティクスを取得することです。次の例について考えてみます。
class Super {} class Sub extends Super {} record R(Super s) {}型Rのすべてのnull以外の値は、式
new R(null)の評価結果の値を含め、パターンR(Super s)に一致すると期待します。(null値がパターンSuper sと一致しないとしても)。ただし、レコード・コンポーネントのnull値がパターンSub sと一致しないため、この値がパターンR(Sub s)と一致することは期待しません。次に、レコード・コンポーネント・パターン・リスト内で出現するパターンの意味は、レコード宣言に関して決定されます。解決では、nullを含むすべての値と一致する必要があるレコード・コンポーネント・パターン・リストに現れるany型のパターンを特別なanyパターンのインスタンスに置き換えます。前述の例では、パターン
R(Sub s)はパターンR(Sub s)に解決されますが、パターンR(Super s)は型Rのレコード・パターンと、anyパターンを含むレコード・コンポーネント・パターン・リストに解決されます。
パターン・マッチングのプロセスには、式の評価または文の実行が関与する場合があります。したがって、式の評価または文の実行が突然完了する場合は、パターン・マッチングが突然完了すると表現されます。突然の完了には常に理由が関連付けられており、それは常に、指定された値を持つthrowです。パターン・マッチングは、突然完了しない場合、正常に完了すると言い表されます。
値がパターンと一致するかどうかを決定するルールおよびパターン変数を初期化するルールは、次のとおりです。
NULL参照は、anyパターンに一致します。
anyパターンによって宣言されるパターン変数は、null参照に初期化されます。
null参照ではない値vは、
ClassCastExceptionの発生なしにvをTにキャストできる場合、型Tのanyパターンと一致します。そうでない場合は、一致しません。vが一致する場合、anyパターンによって宣言されたパターン変数はvに初期化されます。
vが一致しない場合、anyパターンによって宣言されたパターン変数は初期化されません。
null参照は、型パターンと一致しません。
この場合、型パターンによって宣言されたパターン変数は初期化されません。
null参照ではない値vは、
ClassCastExceptionを発生させずにvをTにキャストできる場合、型Tの型パターンと一致します。そうでない場合、一致しません。vが一致する場合、型パターンによって宣言されたパターン変数はvに初期化されます。
vが一致しない場合、型パターンによって宣言されたパターン変数は初期化されません。
値は、含まれているパターンと一致する場合、カッコ付きのパターンと一致します。そうでない場合、一致しません。
null参照は、レコード・パターンと一致しません。
この場合、レコード・パターンによって宣言されたanyパターンの変数は初期化されません。
NULL参照ではない値vは、(i)
ClassCastExceptionの発生なしにvをRにキャストでき、(ii) vがLと一致する場合、型Rのレコードパターンおよびレコード・コンポーネント・パターン・リストLと一致します。それ以外の場合は一致しません。vが一致し、レコード・パターンが名前付きレコード・パターンである場合、レコード・パターン変数はvで初期化されます。
値は、対応するレコード・コンポーネントでリスト内のすべてのパターン(ある場合)と一致する場合、レコード・コンポーネント・パターン・リストと一致します。レコード・コンポーネントで値を照合するパターンが突然完了すると、同じ理由でパターン・マッチングが突然完了します。
null参照でない値vは、その値vのcに対応するアクセサ・メソッドの呼出し結果がパターンpと一致する場合、レコード・コンポーネントcでパターンpと一致します。アクセサ・メソッドの呼出しの実行が理由Sのために突然完了した場合、パターン・マッチングは原因Sによる
MatchExceptionをスローすることで突然完了します。レコード・コンポーネント・パターン・リストに現れるパターンで宣言されたanyパターン変数は、リスト内のすべてのパターンが一致する場合にのみ初期化されます。
null参照である値を対象とするルールはありません。これは、パターン・マッチングを実行する単独のコンストラクトである
instanceofパターン一致演算子(15.20.2)が、値がnull参照ではない場合にのみパターン・マッチングを実行するためです。Javaプログラミング言語の将来のバージョンで、他の式および文でのパターン・マッチングが許可される可能性があります。
14.30.3 パターンのプロパティ
次のいずれかの規則が適用される場合、パターンpは型Tで適用可能であると表現されます。
参照型Uのパターン変数を宣言する型パターンは、TがUにダウンキャスト変換可能(5.5)な場合に、別の参照型Tで適用可能です。
プリミティブ型Pのパターン変数を宣言する型パターンは、型Pで適用可能です。
カッコ付きパターンは、それに含まれているパターンが型Tで適用可能な場合、型Tで適用可能です。
型Rおよびレコード・コンポーネント・パターン・リストLのレコード・パターンは、(i) TがRにダウンキャスト変換可能であり、(ii) L内に現れるすべてのパターンp (ある場合)について、pがRの対応するコンポーネント・フィールドの型で適用可能になっている場合、型Tで適用可能です(5.5)。
型Rで汎用レコード・クラスが指定されている場合、Rがパラメータ化型でない場合はコンパイル時エラーになります。
汎用レコード・クラスを指定する型によるレコード・パターンでは、このクラスのパラメータ化を使用する必要があります。現時点では、レコード・パターンの型引数の推定はサポートされていません。これは、Javaプログラミング言語の将来のバージョンでサポートされる可能性があります。
パターンpは、型Tのすべての値がp (型Tでpが解決(14.30.2)された後)に一致する場合、型Tで無条件であると表現されます。これは、次のように定義されます。
参照タイプSのパターン変数を宣言する型パターンは、TのイレイジャがSのイレイジャのサブタイプである場合、別の参照タイプTで無条件です。
プリミティブ型Pのパターン変数を宣言する型パターンは、型Pで無条件です。
カッコ付きパターンは、それに含まれているパターンが型Tで無条件な場合、型Tで無条件です。
null参照はanyレコード・パターンと一致しないため、レコード・パターンはanyタイプで無条件ではありません。
qと一致するすべての値がpとも一致する場合、パターンpは別のパターンqよりも優先されると表現され、次のように定義されます。
パターンpは、pがTで無条件である場合、型Tのパターン変数を宣言する型パターンよりも優先されます。
pがqより優先される場合、パターンpは、含まれたパターンqがあるカッコ付きのパターンより優先されます。
パターンpは、pがRで無条件である場合、型Rのレコード・パターンよりも優先されます。
型Rおよびレコード・コンポーネント・パターン・リストLのレコード・パターンは、(i) SのイレイジャがRのイレイジャのサブタイプであり、Lのすべてのパターン(ある場合)がMの対応するパターンよりも優先される場合、型Sおよびレコード・コンポーネント・パターン・リストMの別のレコード・パターンよりも優先されます。
第15章: 式
15.20 関係演算子
15.20.2 instanceof演算子
instanceof式は型比較またはパターン・マッチングのいずれかを実行できます。
- InstanceofExpression:
- RelationalExpression
instanceofReferenceType - RelationalExpression
instanceofPattern
instanceofキーワードの右側のオペランドがReferenceTypeである場合、instanceofキーワードは型比較演算子です。
instanceofキーワードの右側のオペランドがPatternである場合、instanceofキーワードはパターン一致演算子です。
次のルールは、instanceofが型比較演算子の場合に適用されます。
式RelationalExpressionの型は、参照型またはnull型である必要があり、そうでない場合、コンパイル時にエラーが発生します。
RelationalExpressionは、ReferenceTypeとダウンキャスト互換性がある必要があり(5.5)、そうでない場合、コンパイル時にエラーが発生します。
実行時に型比較演算子の結果は次のように決定されます。
RelationalExpressionの値がnull参照(4.1)の場合、結果は
falseになります。RelationalExpressionの値がnull参照ではない場合、
ClassCastExceptionを発生させずに値をReferenceTypeにキャストできるときは、結果はtrueです。そうでない場合、falseです。
次のルールは、instanceofがパターン一致演算子の場合に適用されます。
式RelationalExpressionの型は、参照型またはnull型である必要があり、そうでない場合、コンパイル時にエラーが発生します。
RelationalExpressionは、パターンと互換性がある必要があり(14.30.1)、そうでない場合、コンパイル時にエラーが発生します。パターンは、式RelationalExpressionの型(14.30.3)で適用可能である必要があります。それ以外の場合は、コンパイル時にエラーが発生します。RelationalExpressionの型がPattern,の型のサブタイプである場合、コンパイル時にエラーが発生します。パターン・マッチングの実行前に、すべてのパターンが最初に解決されます(14.30.2)。実行可能なパターン一致演算子は、パターンがRelationalExpressionの型で解決されている演算子です。
実行時に、実行可能なパターン一致演算子の結果は次のように決定されます。
RelationalExpressionの値がnull参照の場合、結果は
falseになります。RelationalExpressionの値がnull参照ではないときに、値がパターンと一致する場合(14.30.2)、結果は
trueです。それ以外の場合は、falseです。trueの結果の悪影響は、パターンで宣言されたすべてのパターン変数(ある場合)が初期化されることです。
例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:
caseCaseElement {,CaseElement }default- CaseElement:
- CaseConstant
- Pattern { Guard }
nulldefault
- CaseConstant:
- ConditionalExpression
- Guard:
whenExpression
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.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式(14.11.1.2)は、セレクタ式の最初の評価によって評価されます。その後、次のように処理されます。
セレクタ式の評価が突然完了する場合、
switch式が同じ理由で突然完了します。それ以外の場合、セレクタ式の評価の結果が
nullであり、解決されたswitchブロックのswitchラベルが適用されないときは、NullPointerExceptionがスローされてswitch式全体がその理由で突然完了します。適用するswitchラベルを決定するプロセスが突然完了する場合は、同じ理由でswitch式全体が突然完了します。
- それ以外の場合、セレクタ式の評価の結果が非
nullで、型Character、Byte、ShortまたはIntegerのとき、ボックス化解除変換の対象になります(5.1.8)。この変換が突然完了する場合、switch式全体が同じ理由で突然完了します。
セレクタ式の評価が正常に完了する場合、結果が非nullで後続のボックス化解除変換(ある場合)が正常に完了するとき、switch式の評価は、解決されたswitchブロックに関連付けられた内のswitchラベルがセレクタ式の値と一致するに適用されるかどうかを決定することで続行されます(14.11.1.2)。その後、次のように処理されます。
適用するswitchラベルを決定するプロセスが突然完了する場合は、同じ理由で
switch式全体が突然完了します。一致する適用されるswitchラベルがなく、セレクタ式の評価結果がenum型の場合は、IncompatibleClassChangeErrorがスローされ、その理由からswitch式全体が突然完了します。適用されるswitchラベルがなく、セレクタ式の評価結果がenum型でない場合は、
MatchExceptionがスローされ、その理由のために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文が網羅的でない場合や、switchブロックがswitchラベルの後のdefaultラベルが存在しない}セパレータで終了している場合は、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ラベルが付いた文グループではないブロック文の前で割当て[解除]されます。