0Pricing
Java Academy · レッスン

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フィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. Sealed 型の宣言
  2. permits 節
  3. Record と Sealed 型
  4. Sealed 型に対する網羅的な switch
← Java Academyに戻る