Menu
Coddy logo textTech

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 クラスを使用してください。ただし、多用しないでください。ほとんどのクラスは、拡張に対して開かれたままにしておくことでメリットが得られます。

challenge icon

チャレンジ

簡単

クラスの継承をいつ、なぜ防ぐべきかを示す、安全な設定システムを構築しましょう。アプリケーション設定を管理する final クラスを作成し、継承によってその動作が変更されないようにします。

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

  • config.dart: ここで設定クラスを定義します:
    • 継承できない final class AppConfigString appNameString version プロパティ、両方の値を受け取るコンストラクタ、および [appName] v[version] を出力する display() メソッドを持つ必要があります。
    • 通常の(final ではない)Feature クラス。String namebool 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 が trueFeature を作成し、status() を呼び出します。
    • 空行を出力します。
    • name が 'AI Assistant'、enabled が false、warning が 'May be unstable'ExperimentalFeature を作成し、status() を呼び出します。

AppConfigfinal としてマークされていることに注目してください。これは、その設定動作が一貫しており安全である必要があるためです。サブクラスが設定の管理方法を変更できないようにしています。一方で、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() を呼び出してください
}
quiz icon腕試し

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

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