Sealed クラス (Dart 3)
CoddyのDartジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 85/110。
Dart 3 では、どのクラスがそれらを継承または実装できるかを制限するシールクラス (sealed classes) が導入されました。クラスを sealed としてマークすると、同じファイル内のクラスのみがその直接のサブタイプになることができます。これにより、既知のサブタイプの閉じられたセットが作成されます。
sealed class Result {}
class Success extends Result {
final String data;
Success(this.data);
}
class Failure extends Result {
final String error;
Failure(this.error);
}シールドクラスの強力さは、網羅性チェックにあります。シールド型をswitch文で使用すると、コンパイラはすべての可能なサブタイプを把握し、それぞれを確実に処理するように促します:
String handleResult(Result result) {
return switch (result) {
Success(data: var d) => 'Got: $d',
Failure(error: var e) => 'Error: $e',
};
// デフォルトは不要です - コンパイラはすべてのケースがカバーされていることを認識しています!
}
void main() {
var success = Success('Hello');
var failure = Failure('Not found');
print(handleResult(success)); // Got: Hello
print(handleResult(failure)); // Error: Not found
}シールドクラスに新しいサブタイプを追加すると、コンパイラはそのサブタイプを処理していないすべての switch 文にフラグを立てます。これにより、シールドクラスは状態や結果、あるいは固定されたバリアントのセットを持つあらゆるシナリオのモデリングに最適となります。抽象クラスとは異なり、シールドクラスは外部のコードが予期しないサブタイプを追加できないことを保証します。
チャレンジ
簡単sealedクラスを使用して、ネットワークリクエストハンドラーを構築しましょう!ネットワークレスポンスのさまざまな状態をモデル化する型安全なシステムを作成し、考えられるすべての結果が適切に処理されるようにします。
コードを2つのファイルに整理します:
response.dart: ネットワークレスポンスのすべての可能な状態を表すsealedクラスの階層を作成します:- すべてのレスポンスタイプのベースとなる
NetworkResponseという名前のsealedクラス String messageフィールド(例: "Fetching data...")を持つ、NetworkResponseを継承したLoadingクラスString dataフィールドとint statusCodeフィールドを持つ、NetworkResponseを継承したSuccessクラスString errorMessageフィールドとint errorCodeフィールドを持つ、NetworkResponseを継承したErrorクラス
- すべてのレスポンスタイプのベースとなる
main.dart: レスポンスファイルをインポートし、switch式を使用してすべてのレスポンスタイプを処理する関数を作成します:Stringを返すhandleResponse(NetworkResponse response)関数を作成します- パターンマッチングを用いたswitch式を使用して、各ケースを処理します:
Loadingの場合:Status: [message]を返しますSuccessの場合:Success ([statusCode]): [data]を返しますErrorの場合:Error ([errorCode]): [errorMessage]を返します
- 3つのレスポンスインスタンスを作成し、それぞれの処理結果をプリントします:
- メッセージが
Please wait...のLoading - データが
User profile loadedでステータスコードが200のSuccess - エラーメッセージが
Not foundでエラーコードが404のError
- メッセージが
sealedクラスの素晴らしい点は、コンパイラがすべての可能なレスポンスタイプを処理していることを保証してくれるため、デフォルトケース(default case)が不要になることです!
期待される出力:
Status: Please wait...
Success (200): User profile loaded
Error (404): Not foundチートシート
シールドクラス(Sealed classes)は、どのクラスがそれらを継承または実装できるかを制限します。同じファイル内のクラスのみが直接のサブタイプになることができ、既知のバリアントの閉じたセットを作成します。
sealed class Result {}
class Success extends Result {
final String data;
Success(this.data);
}
class Failure extends Result {
final String error;
Failure(this.error);
}シールドクラスは、switch式における網羅性チェック(exhaustiveness checking)を可能にします。コンパイラはすべての可能なサブタイプを把握しており、それぞれを確実に処理するようにします:
String handleResult(Result result) {
return switch (result) {
Success(data: var d) => 'Got: $d',
Failure(error: var e) => 'Error: $e',
};
// No default case needed - all cases are covered
}新しいサブタイプを追加すると、コンパイラはそれを処理していないすべてのswitch文にフラグを立てます。これにより、シールドクラスは、すべてのケースが処理されていることをコンパイル時に保証したい状態、結果、または固定されたバリアントのセットをモデリングするのに最適です。
自分で試してみよう
import 'response.dart';
// TODO: 関数 handleResponse(NetworkResponse response) を作成してください
// これは String を返します
// パターンマッチングを用いた switch 式を使用して、各ケースを処理してください:
// - Loading の場合: "Status: [message]" を返す
// - Success の場合: "Success ([statusCode]): [data]" を返す
// - Error の場合: "Error ([errorCode]): [errorMessage]" を返す
void main() {
// TODO: メッセージが "Please wait..." の Loading インスタンスを作成してください
// TODO: データが "User profile loaded" でステータスコードが 200 の Success インスタンスを作成してください
// TODO: エラーメッセージが "Not found" でエラーコードが 404 の Error インスタンスを作成してください
// TODO: 各レスポンスを処理した結果をプリントしてください
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
4Null Safety
Null Safety 入門Nullable と Non-Nullable? 演算子と ! 演算子late キーワードと Null SafetyNull-Aware 演算子クラスにおける Null Safetyまとめ:ユーザープロフィールシステム10コレクションとジェネリクス
List, Set, Map の概要型安全なコレクションジェネリッククラスジェネリックメソッドジェネリクスの制約Iterable と Iteratorまとめ:ジェネリックなストレージ13高度な OOP の概念
コンポジション vs 継承拡張メソッドCallable クラスSealed クラス (Dart 3)レコード (Dart 3)パターンとマッチング (3.0)メソッドを持つ Enum