Menu
Coddy logo textTech

シールドクラス (Java 17+)

CoddyのJavaジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 52/87。

従来のJavaでは、クラスを作成すると、(finalとマークしない限り)他のどのクラスでもそれを継承できます。しかし、継承は許可したいが、誰からでもというわけではない場合はどうすればよいでしょうか?Java 17では、どのクラスが自分のクラスを継承できるかを正確に制御できるようにするために、封印クラス(sealed classes)が導入されました。

シールドクラスは、sealedおよびpermitsキーワードを使用して、許可されたサブクラスを明示的に宣言します:

sealed class Shape permits Circle, Rectangle, Triangle {
    abstract double area();
}

final class Circle extends Shape {
    double radius;
    double area() { return Math.PI * radius * radius; }
}

final class Rectangle extends Shape {
    double width, height;
    double area() { return width * height; }
}

final class Triangle extends Shape {
    double base, height;
    double area() { return 0.5 * base * height; }
}

CircleRectangle、および Triangle のみが Shape を拡張できます。それ以外のクラスが拡張しようとすると、コンパイルエラーが発生します。

許可された各サブクラスは、3つの修飾子のうちの1つを使用する必要があります:final(これ以上の拡張は不可)、sealed(制限チェーンを継続)、またはnon-sealed(無制限の拡張を許可):

sealed class Vehicle permits Car, Truck { }

final class Car extends Vehicle { }           // 拡張不可
non-sealed class Truck extends Vehicle { }    // 誰でもTruckを拡張可能

シールドクラスは、コンパイラがすべての可能なサブタイプを把握し、網羅性を検証できるため、switch式でのパターンマッチングにおいて特に強力です。これらは、支払い方法、レスポンスタイプ、図形など、ドメイン内の固定された型のセットをモデリングする場合に最適です。

challenge icon

チャレンジ

簡単

アプリケーション内に存在できる通知の種類を制限することで、シール(封印)クラスを実証する通知システムを構築しましょう。特定の通知タイプのみが許可される階層を作成し、各タイプが異なる方法で配信を処理するようにします。

コードを4つのファイルに分けて構成します:

  • Notification.java: すべての通知のベースとなるシール抽象クラスを作成します。これは、EmailNotificationSMSNotification、および PushNotification のちょうど3つのサブクラスを許可する必要があります。シールクラスには、protectedフィールドの message (String)、それを初期化するためのコンストラクタ、メッセージのゲッター、および通知の送信方法を説明するStringを返す抽象メソッド deliver() が必要です。
  • EmailNotification.java: Notificationを継承する final クラスを作成します。EmailNotificationには、メールアドレス用の追加のprivateフィールド recipient (String) があります。そのコンストラクタはメッセージと受信者の両方を受け取り、super() を使用して親コンストラクタを呼び出す必要があります。deliver() メソッドは、"Sending email to " + recipient + ": " + message を返す必要があります。
  • SMSNotification.java: Notificationを継承する non-sealed クラスを作成します。これにより、将来必要に応じて他のクラスがこれを継承できるようになります。SMSNotificationには、追加のprivateフィールド phoneNumber (String) があります。そのコンストラクタはメッセージと電話番号を受け取ります。deliver() メソッドは、"Sending SMS to " + phoneNumber + ": " + message を返す必要があります。
  • PushNotification.java: Notificationを継承する final クラスを作成します。PushNotificationには、追加のprivateフィールド deviceId (String) があります。そのコンストラクタはメッセージとデバイスIDを受け取ります。deliver() メソッドは、"Sending push to device " + deviceId + ": " + message を返す必要があります。
  • Main.java: 通知システムを完成させましょう!メッセージ (String)、メールアドレス (String)、電話番号 (String)、およびデバイスID (String) の4つの入力を受け取ります。

    同じメッセージを使用し、それぞれの連絡先情報を使用して、各通知タイプのインスタンスを1つずつ作成します。シールクラスによるポリモーフィズムを実証するために、3つすべてを Notification[] 型の配列に格納します。次に、配列を反復処理し、各通知で deliver() を呼び出した結果を出力します。

メッセージ、メールアドレス、電話番号、デバイスIDの順に4つの入力を受け取ります。

シールクラスがどのように階層を制限しているかに注目してください。許可された3つのクラスのみが Notification を継承できます。各サブクラスは、自身を finalsealed、または non-sealed として宣言する必要があります。これにより、ポリモーフィズムを有効にしながら、型階層を完全に制御できるようになります!

チートシート

シールドクラス(sealed class)は、sealedおよびpermitsキーワードを使用して、どのクラスがそれを拡張できるかを制限します:

sealed class Shape permits Circle, Rectangle, Triangle {
    abstract double area();
}

permitsにリストされているクラスのみが、そのシールドクラスを拡張できます。それ以外のクラスが拡張しようとすると、コンパイルエラーが発生します。

許可された各サブクラスは、次の3つの修飾子のいずれかを使用する必要があります:

  • final - さらなる拡張を禁止します
  • sealed - 独自の許可されたサブクラスを使用して制限チェーンを継続します
  • non-sealed - 制限のない拡張のために階層を公開します
sealed class Vehicle permits Car, Truck { }

final class Car extends Vehicle { }           // 拡張できません
non-sealed class Truck extends Vehicle { }    // 誰でもTruckを拡張できます

シールドクラスは、支払い方法、レスポンスタイプ、幾何学図形など、ドメイン内の固定された型のセットをモデリングするのに役立ちます。コンパイラがすべての可能なサブタイプを把握しているため、switch式のパターンマッチングとうまく連携します。

自分で試してみよう

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // 4つの入力を読み込む
        String message = scanner.nextLine();
        String email = scanner.nextLine();
        String phoneNumber = scanner.nextLine();
        String deviceId = scanner.nextLine();
        
        // TODO: 同じ message を使用して、各通知タイプのインスタンスを1つずつ作成する
        // - message と email を使用した EmailNotification
        // - message と phoneNumber を使用した SMSNotification
        // - message と deviceId を使用した PushNotification
        
        // TODO: 3つすべての通知を Notification[] 配列に格納する
        // これは sealed クラスを用いたポリモーフィズムのデモンストレーションです
        
        // TODO: 配列をループで回し、各通知の deliver() の結果を出力する
        // 各通知に対して
        
    }
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン