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 classを使用してネットワークリクエストハンドラーを構築しましょう!ネットワークレスポンスのさまざまな状態をモデル化する型安全なシステムを作成し、考えられるすべての結果が適切に処理されるようにします。
コードを2つのファイルに整理します:
response.dart: ネットワークレスポンスのすべての可能な状態を表すsealed classの階層を作成します:- すべてのレスポンスタイプのベースとなる
NetworkResponseという名前のsealed class 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つのレスポンスインスタンスを作成し、それぞれの処理結果を出力します:
- message が
Please wait...のLoading - data が
User profile loadedで statusCode が200のSuccess - errorMessage が
Not foundで errorCode が404のError
- message が
sealed classの素晴らしい点は、コンパイラがすべての可能なレスポンスタイプを処理していることを保証してくれるため、defaultケースが不要になることです!
期待される出力:
Status: Please wait...
Success (200): User profile loaded
Error (404): Not found自分で試してみよう
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