0Pricing
Java Academy · レッスン

Record と Sealed 型

閉じた階層構造をモデル化します。

「Record と Sealed 型」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。

sealedとレコードの組み合わせ

sealedインターフェースとレコードは、相性のよい組み合わせです。sealedインターフェースでケースの閉じた集合を定義し、各レコードで具体的かつ不変なケースを表します。これらを組み合わせることで、代数的データ型をモデル化できます。

レコードは暗黙的にfinal

レコードは自動的にfinalになるため、許可されるサブタイプがfinal、sealed、non-sealedのいずれかでなければならないという規則を満たします。追加の修飾子は必要ありません。

閉じた形状の継承階層

形状をsealedインターフェースを実装するレコードとしてモデル化し、それぞれに固有のデータを持たせます。

public class Main {
    sealed interface Shape permits Circle, Rectangle {}
    record Circle(double radius) implements Shape {}
    record Rectangle(double width, double height) implements Shape {}

    public static void main(String[] args) {
        Shape s = new Rectangle(3, 4);
        System.out.println(s);
    }
}

振る舞いの追加

sealedインターフェースで、すべてのレコードが実装するメソッドを宣言できます。これにより、閉じた集合に対してポリモーフィズムを利用できます。

public class Main {
    sealed interface Shape permits Circle, Rectangle {
        double area();
    }
    record Circle(double radius) implements Shape {
        public double area() { return Math.PI * radius * radius; }
    }
    record Rectangle(double width, double height) implements Shape {
        public double area() { return width * height; }
    }

    public static void main(String[] args) {
        Shape s = new Circle(2);
        System.out.printf("Area: %.2f%n", s.area());
    }
}

equalsとtoStringの自動生成

レコードはequals、hashCode、toStringを自動的に生成します。同じコンポーネントを持つ2つのレコードは等しいため、値のモデル化に適しています。

public class Main {
    sealed interface Shape permits Circle {}
    record Circle(double radius) implements Shape {}

    public static void main(String[] args) {
        Circle a = new Circle(5);
        Circle b = new Circle(5);
        System.out.println(a.equals(b));
        System.out.println(a);
    }
}

結果のモデル化

よくあるパターンとして、成功レコードと失敗レコードを持つsealedな結果型があります。これは、予想される結果を表す場合に、nullを返したり例外を使ったりする方法の代わりになります。

public class Main {
    sealed interface Result permits Ok, Err {}
    record Ok(int value) implements Result {}
    record Err(String message) implements Result {}

    static Result parse(String s) {
        try { return new Ok(Integer.parseInt(s)); }
        catch (NumberFormatException e) { return new Err("bad number: " + s); }
    }

    public static void main(String[] args) {
        System.out.println(parse("42"));
        System.out.println(parse("oops"));
    }
}

検証のためのコンパクトコンストラクター

レコードでは、コンパクトコンストラクターで入力値を検証できます。これにより、それぞれのケースを生成時点で正しい状態に保てます。

public class Main {
    sealed interface Shape permits Circle {}
    record Circle(double radius) implements Shape {
        Circle {
            if (radius < 0) throw new IllegalArgumentException("radius must be >= 0");
        }
    }

    public static void main(String[] args) {
        try { new Circle(-1); }
        catch (IllegalArgumentException e) { System.out.println(e.getMessage()); }
    }
}

再帰的なデータモデル

レコードはsealedインターフェースを参照できるため、式ツリーや連結リストのような再帰的な構造を構築できます。

public class Main {
    sealed interface Expr permits Num, Add {}
    record Num(int value) implements Expr {}
    record Add(Expr left, Expr right) implements Expr {}

    public static void main(String[] args) {
        Expr e = new Add(new Num(1), new Add(new Num(2), new Num(3)));
        System.out.println(e);
    }
}

不変性の利点

レコードは浅い意味で不変なので、レコードによるsealedな継承階層は理解しやすく、安全に共有でき、並行処理にも適しています。レコード自体を防御的にコピーする必要はありません。

switchとの組み合わせ

sealedレコードは、パターンマッチングを使うswitchと組み合わせると特に効果を発揮します。各ケースでレコードを分解でき、コンパイラーがすべてのケースを網羅しているか確認します。この内容はパターンのコースで詳しく扱います。

モデルに対する計算

再帰的なレコードのツリーをたどるメソッドとして、インタープリターを記述できます。

public class Main {
    sealed interface Expr permits Num, Add {}
    record Num(int value) implements Expr {}
    record Add(Expr left, Expr right) implements Expr {}

    static int eval(Expr e) {
        if (e instanceof Num n) return n.value();
        Add a = (Add) e;
        return eval(a.left()) + eval(a.right());
    }

    public static void main(String[] args) {
        Expr e = new Add(new Num(4), new Add(new Num(5), new Num(6)));
        System.out.println(eval(e));
    }
}

理解度チェック

sealedレコードについての理解度を確認しましょう。

まとめ

sealed型とレコードを組み合わせる方法を学びました。

  • レコードは暗黙的にfinalなので、許可されるサブタイプとしてそのまま組み込めます。
  • sealedインターフェースで共有メソッドを宣言できます。
  • レコードによって、等価性、ハッシュ、toStringが自動的に提供されます。
  • これらを組み合わせると、閉じた不変の、場合によっては再帰的なデータをモデル化できます。

よくある質問

「Record と Sealed 型」レッスンは無料ですか?

はい。「Record と Sealed 型」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。

「Record と Sealed 型」で何を学びますか?

閉じた階層構造をモデル化します。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Java Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「Record と Sealed 型」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このJava Academyレッスンでコードを書いて実行できますか?

はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

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

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