Menu
Coddy logo textTech

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

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

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

sealed class は、sealed キーワードと permits キーワードを使用して、許可されるサブクラスを明示的に宣言します。

sealed abstract 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; }
}

CircleRectangleTriangleのみがShapeを拡張できます。その他のclassがそれを拡張しようとすると、コンパイルエラーが発生します。

許可された各サブクラスは、3つの修飾子のいずれかを使用する必要があります: final(それ以上のextensionなし)、sealed(制限チェーンを継続)、またはnon-sealed(制限のないextensionを可能にする):

sealed class Vehicle permits Car, Truck { }

final class Car extends Vehicle { }           // 拡張できない
non-sealed class Truck extends Vehicle { }    // 誰でもTruckを拡張できる

Sealed classesは、switch式でのパターンマッチングと特に相性がよく、コンパイラーはすべての可能なサブタイプを把握しているため、網羅性を検証できます。支払い方法、レスポンスタイプ、幾何学的図形など、ドメイン内で固定されたタイプの集合をモデル化する場合に最適です。

challenge icon

チャレンジ

簡単

アプリケーション内に存在できる通知の種類を制限することで、sealed class を使用した通知システムを構築しましょう。特定の通知タイプだけが許可される階層を作成し、それぞれのタイプで配信方法を変えます。

コードをfourつのファイルに整理します。

  • Notification.java: すべての通知のbaseとなるsealed abstract classをCreateします。正確にthreeつのサブクラス、EmailNotificationSMSNotificationPushNotificationだけを許可します。sealed classにはprotected field message(String)、それをinitializeするconstructor、messageのgetter、および通知の送信方法を説明するStringを返すabstract method deliver()が必要です。
  • EmailNotification.java: Notificationをextendするfinal classをCreateします。EmailNotificationには、email address用の追加のprivate field recipient(String)があります。そのconstructorはmessageとrecipientの両方を受け取り、super()を使用してparent constructorを呼び出します。deliver() methodは次を返す必要があります: Sending email to [recipient]: [message]
  • SMSNotification.java: Notificationをextendするnon-sealed classをCreateします。これにより、必要であればfutureに他のclassがこれをextendできます。SMSNotificationには、追加のprivate field phoneNumber(String)があります。そのconstructorはmessageとphone numberを受け取ります。deliver() methodは次を返す必要があります: Sending SMS to [phoneNumber]: [message]
  • PushNotification.java: Notificationをextendするfinal classをCreateします。PushNotificationには、追加のprivate field deviceId(String)があります。そのconstructorはmessageとdevice IDを受け取ります。deliver() methodは次を返す必要があります: Sending push to device [deviceId]: [message]
  • Main.java: notification systemをまとめましょう。fourつのinputsを受け取ります。message(String)、email address(String)、phone number(String)、device ID(String)です。

    同じmessageを使用し、それぞれのcontact informationを指定して、各notification typeを1つずつCreateします。sealed classesによるpolymorphismを示すため、threeつすべてをNotification[]型のarrayに格納します。その後、arrayをIterateし、各notificationでdeliver()を呼び出した結果を出力します。

fourつのinputsを、message、email address、phone number、device IDの順に受け取ります。

sealed classがhierarchyをどのように制限しているかに注目してください。Notificationをextendできるのは、許可されたthreeつのclassだけです。各サブクラスは自身をfinalsealed、またはnon-sealedとして宣言する必要があります。これにより、polymorphismを有効にしながら、type hierarchyを完全に制御できます。

自分で試してみよう

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: 同じメッセージを使用して各通知タイプを1つずつ作成する
        // - messageとemailを使用したEmailNotification
        // - messageとphoneNumberを使用したSMSNotification
        // - messageとdeviceIdを使用したPushNotification
        
        // TODO: 3つの通知すべてをNotification[]配列に格納する
        // これはシールドクラスによるポリモーフィズムを示しています
        
        // TODO: 配列を反復処理し、deliver()の結果を出力する
        // 各通知について
        
    }
}
quiz icon腕試し

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

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

自分で練習してみよう: Javaオンラインコンパイラ