final クラスキーワード
CoddyのDartジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 40/110。
クラスが継承されるのを防ぎたい場合があります。finalキーワードをクラスに適用すると、他のクラスがそのクラスを継承できなくなります。
final class DatabaseConnection {
String connectionString;
DatabaseConnection(this.connectionString);
void connect() {
print('Connected to $connectionString');
}
}
// これはコンパイルエラーを引き起こします:
// class CustomConnection extends DatabaseConnection { }クラスを final とマークすることは、そのクラスを継承することで意図された動作やセキュリティの保証が損なわれる可能性がある場合に有用です。例えば、機密性の高いリソースを管理するクラスは、そのメソッドが予期しない方法でオーバーライドされないようにする必要があるかもしれません。
Dart 3では、関連する修飾子も導入されました。baseはサブクラスに(implementsではなく)extendsの使用を強制し、sealedは継承を同じライブラリ内に制限します。今のところは、クラスを完全にロックする最も簡単な方法としてfinalに注目しましょう。
クラスの設計が完了しており、継承が問題を引き起こすだけであると確信している場合は、final クラスを使用してください。ただし、多用しないでください。ほとんどのクラスは、拡張に対して開かれたままにしておくことでメリットが得られます。
チャレンジ
簡単クラスの継承をいつ、なぜ防ぐべきかを示す、安全な設定システムを構築しましょう。アプリケーション設定を管理する final クラスを作成し、継承によってその動作が変更されないようにします。
コードを2つのファイルに整理します:
config.dart: ここで設定クラスを定義します:- 継承できない
final class AppConfig。String appNameとString versionプロパティ、両方の値を受け取るコンストラクタ、および[appName] v[version]を出力するdisplay()メソッドを持つ必要があります。 - 通常の(final ではない)
Featureクラス。String nameとbool enabledプロパティ、両方のコンストラクタ、および[name]: [enabled ? 'ON' : 'OFF']を出力するstatus()メソッドを持ちます。 Featureを継承し、String warningプロパティを追加するExperimentalFeatureクラス。@overrideを使用してstatus()をオーバーライドし、最初にsuper.status()で親のバージョンを呼び出し、次にWarning: [warning]を出力します。
- 継承できない
main.dart: 設定ファイルをインポートし、final クラスと通常のクラスの違いを示します:- appName が
'MyApp'、version が'2.0.1'のAppConfigを作成し、display()を呼び出します。 - 空行を出力します。
- name が
'Dark Mode'、enabled がtrueのFeatureを作成し、status()を呼び出します。 - 空行を出力します。
- name が
'AI Assistant'、enabled がfalse、warning が'May be unstable'のExperimentalFeatureを作成し、status()を呼び出します。
- appName が
AppConfig が final としてマークされていることに注目してください。これは、その設定動作が一貫しており安全である必要があるためです。サブクラスが設定の管理方法を変更できないようにしています。一方で、Feature は拡張可能なままであり、ExperimentalFeature が特化した動作を追加できるようになっています。
期待される出力:
MyApp v2.0.1
Dark Mode: ON
AI Assistant: OFF
Warning: May be unstableチートシート
final キーワードは、クラスが継承されるのを防ぎます:
final class DatabaseConnection {
String connectionString;
DatabaseConnection(this.connectionString);
void connect() {
print('Connected to $connectionString');
}
}
// これはコンパイルエラーを引き起こします:
// class CustomConnection extends DatabaseConnection { }継承によって意図した動作やセキュリティの保証が損なわれる可能性がある場合に、final クラスを使用します。特定の制限する理由がない限り、ほとんどのクラスは継承のためにオープンにしておくべきです。
Dart 3 では、base(サブクラスに extends の使用を強制する)および sealed(継承を同じライブラリ内に制限する)修飾子も提供されています。
自分で試してみよう
import 'config.dart';
void main() {
// TODO: appName が 'MyApp'、version が '2.0.1' の AppConfig を作成してください
// その後、display() を呼び出してください
// TODO: 空行を出力してください
// TODO: name が 'Dark Mode'、enabled が true の Feature を作成してください
// その後、status() を呼び出してください
// TODO: 空行を出力してください
// TODO: name が 'AI Assistant'、
// enabled が false、warning が 'May be unstable' のものを作成してください
// その後、status() を呼び出してください
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
4Null Safety
Null Safety 入門Nullable と Non-Nullable? 演算子と ! 演算子late キーワードと Null SafetyNull-Aware 演算子クラスにおける Null Safetyまとめ:ユーザープロフィールシステム10コレクションとジェネリクス
List, Set, Map の概要型安全なコレクションジェネリッククラスジェネリックメソッドジェネリクスの制約Iterable と Iteratorまとめ:ジェネリックなストレージ