Sealed 型に対する網羅的な switch
コンパイル時に網羅性を確認します。
「Sealed 型に対する網羅的な switch」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。
網羅的なswitch
sealed型に対してswitchを使うと、コンパイラーは許可されたすべてのサブタイプを把握しています。すべてを処理するswitchは網羅的であり、default分岐は必要ありません。
型パターンswitch
switch式では、型パターンを使ってsealedな値の実行時の型に基づいて分岐できます。
public class Main {
sealed interface Shape permits Circle, Square {}
record Circle(double radius) implements Shape {}
record Square(double side) implements Shape {}
static String describe(Shape s) {
return switch (s) {
case Circle c -> "circle r=" + c.radius();
case Square sq -> "square s=" + sq.side();
};
}
public static void main(String[] args) {
System.out.println(describe(new Circle(2)));
System.out.println(describe(new Square(3)));
}
}defaultは不要
CircleとSquareだけが許可されたサブタイプなので、コンパイラーはこのswitchを完全なものとして受け入れます。defaultを追加しても冗長です。
コンパイル時の完全性
後から新しい許可サブタイプを追加し、それを処理し忘れると、網羅的なswitchはすべてコンパイルに失敗します。これにより、実行時バグの大きな分類をコンパイルエラーに変えられます。
結果の計算
網羅的なswitch式は、値から面積のような結果を計算するのに適しています。
public class Main {
sealed interface Shape permits Circle, Square {}
record Circle(double radius) implements Shape {}
record Square(double side) implements Shape {}
static double area(Shape s) {
return switch (s) {
case Circle c -> Math.PI * c.radius() * c.radius();
case Square sq -> sq.side() * sq.side();
};
}
public static void main(String[] args) {
System.out.printf("%.2f%n", area(new Circle(1)));
System.out.printf("%.2f%n", area(new Square(2)));
}
}switch文とswitch式の違い
網羅性が適用されるのは、値を生成するswitch式と、sealed型に対してアロー形式を使うswitch文です。コンパイラーがすべてのケースを処理していることを強制します。
nullの処理
デフォルトでは、参照型の値がnullの場合、switchはNullPointerExceptionをスローします。case nullラベルを追加すれば、明示的に処理できます。
public class Main {
sealed interface Shape permits Circle, Square {}
record Circle(double radius) implements Shape {}
record Square(double side) implements Shape {}
static String describe(Shape s) {
return switch (s) {
case null -> "no shape";
case Circle c -> "circle";
case Square sq -> "square";
};
}
public static void main(String[] args) {
System.out.println(describe(null));
System.out.println(describe(new Circle(1)));
}
}ガードが網羅性を損なわない理由
whenガードを追加してケースをより細かく条件分けできますが、ガード付きのケースだけでは型を処理したことになりません。網羅性を保つには、各サブタイプに対してガードのないケースも必要です。
public class Main {
sealed interface Shape permits Circle, Square {}
record Circle(double radius) implements Shape {}
record Square(double side) implements Shape {}
static String size(Shape s) {
return switch (s) {
case Circle c when c.radius() > 10 -> "big circle";
case Circle c -> "small circle";
case Square sq -> "square";
};
}
public static void main(String[] args) {
System.out.println(size(new Circle(20)));
System.out.println(size(new Circle(2)));
}
}sealedクラスに対するswitch
この網羅性は、インターフェースだけでなくsealedクラスでも同じように機能します。
public class Main {
sealed static abstract class Token permits Word, Number {}
static final class Word extends Token { final String text; Word(String t){text=t;} }
static final class Number extends Token { final int value; Number(int v){value=v;} }
static String show(Token t) {
return switch (t) {
case Word w -> "word:" + w.text;
case Number n -> "num:" + n.value;
};
}
public static void main(String[] args) {
System.out.println(show(new Word("hi")));
System.out.println(show(new Number(7)));
}
}強力な理由
網羅的なswitchとsealed型を組み合わせると、安全で変更に対応しやすいコードになります。ケースの集合が1か所に記録され、モデルが拡張されたときにケースを処理し忘れていないことをコンパイラーが保証します。
完全な例
ここでは、sealedな結果型をすべてのケースを処理しながらメッセージに変換しています。
public class Main {
sealed interface Result permits Ok, Err {}
record Ok(int value) implements Result {}
record Err(String message) implements Result {}
static String render(Result r) {
return switch (r) {
case Ok ok -> "value = " + ok.value();
case Err err -> "error: " + err.message();
};
}
public static void main(String[] args) {
System.out.println(render(new Ok(10)));
System.out.println(render(new Err("bad input")));
}
}理解度チェック
網羅的なswitchについての理解度を確認しましょう。
まとめ
sealed型に対する網羅的なswitchについて学びました。
- 許可されたすべてのサブタイプを処理するとswitchが網羅的になるため、
defaultは必要ありません。 - 新しいサブタイプの処理漏れはコンパイルエラーになります。
case nullを使うと、nullを明示的に処理できます。- ガードはケースを細かくできますが、ガードのないケースの代わりにはなりません。
よくある質問
「Sealed 型に対する網羅的な switch」レッスンは無料ですか?
はい。「Sealed 型に対する網羅的な switch」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。
「Sealed 型に対する網羅的な switch」で何を学びますか?
コンパイル時に網羅性を確認します。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Java Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。
「Sealed 型に対する網羅的な switch」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このJava Academyレッスンでコードを書いて実行できますか?
はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- Sealed 型の宣言
- permits 節
- Record と Sealed 型
- Sealed 型に対する網羅的な switch