Menu
Coddy logo textTech

late キーワードと Null Safety

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

非 null 型の変数が必要だが、すぐにその値を代入できない場合があります。late キーワードは Dart に対して「これを使用する前に必ず初期化することを約束します」と伝えます。

late String description;

void setup() {
  description = 'Initialized later';
}

void main() {
  setup();
  print(description);  // 正常に動作する - 使用前に初期化済み
}

late がないと、Dart は description が初期化されていないとエラーを出します。late を使用すると、アクセスする前に値が設定されていることを保証する責任を負うことになります。初期化する前に読み取ろうとすると、実行時エラーが発生します。

よくあるユースケースは、実際に必要になるまで実行を遅らせたい重い計算です:

class DataProcessor {
  late List<int> data = _loadData();
  
  List<int> _loadData() {
    print('Loading data...');
    return [1, 2, 3, 4, 5];
  }
}

DataProcessor processor = DataProcessor();
// 'Loading data...' はまだ出力されていません
print(processor.data);  // ここでロードされ、出力されます

_loadData() メソッドは、オブジェクトの作成時ではなく、data が最初にアクセスされたときにのみ実行されます。この遅延初期化は、値が一度も必要とされない可能性がある場合に、パフォーマンスを向上させることができます。

late は、変数が使用される前に初期化されることが確実であるものの、宣言時にそれを行うことができない場合に使用します。これはコンパイラへの約束であり、その約束を破るとプログラムはクラッシュします。

challenge icon

チャレンジ

簡単

遅延初期化のための late 変数の威力を示す構成マネージャー(Configuration Manager)を作成しましょう。構成が実際に必要になったときにのみ、コストのかかるセットアップ操作が実行されるシステムを作成します。

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

  • config_manager.dart: アプリケーション設定を処理する ConfigManager クラスを定義します。このクラスには以下を含める必要があります:
    • コンストラクタを介して渡される String appName
    • セットアップメソッドを介して後で初期化される late String databaseUrl
    • 最初にアクセスされたときに 'Calculating max connections...' と出力し、100 を返す遅延初期化を伴う late int maxConnections
    • databaseUrl を初期化する setupDatabase(String url) メソッド
    • 現在の構成を出力する displayConfig() メソッド
  • main.dart: 構成マネージャーをインポートし、late変数がどのように機能するかを示します:
    • アプリ名を 'MyApp' として ConfigManager を作成します
    • 'Config created' と出力します
    • URL 'localhost:5432/mydb' を指定して setupDatabase を呼び出します
    • 'Accessing max connections...' と出力します
    • maxConnections の値を出力します
    • 'Accessing max connections again...' と出力します
    • 再度 maxConnections の値を出力します(計算メッセージが繰り返されないことに注目してください)
    • displayConfig() を呼び出します

displayConfig() メソッドは、次の形式で出力する必要があります:

--- Configuration ---
App: [appName]
Database: [databaseUrl]
Max Connections: [maxConnections]

遅延初期化される maxConnections の計算が、最初にアクセスしたときの1回しか実行されないことに注目してください。これが初期化子を伴う late の利点です!

期待される出力:

Config created
Accessing max connections...
Calculating max connections...
100
Accessing max connections again...
100
--- Configuration ---
App: MyApp
Database: localhost:5432/mydb
Max Connections: 100

チートシート

late キーワードを使用すると、使用される前に後で初期化される非 null 許容変数を宣言できます。

late String description;

void setup() {
  description = 'Initialized later';
}

void main() {
  setup();
  print(description);  // 動作する - 使用前に初期化されている
}

late がない場合、Dart は即時の初期化を要求します。late を使用すると、変数にアクセスする前に初期化することを約束します。初期化されていない late 変数を読み取ると、実行時エラーが発生します。

遅延初期化: late 変数に初期化子がある場合、初期化はその変数に最初にアクセスされるまで延期されます。

class DataProcessor {
  late List<int> data = _loadData();
  
  List<int> _loadData() {
    print('Loading data...');
    return [1, 2, 3, 4, 5];
  }
}

DataProcessor processor = DataProcessor();
// 'Loading data...' はまだ出力されていません
print(processor.data);  // ここでロードされ、出力されます

_loadData() メソッドは、オブジェクトが作成されたときではなく、data に最初にアクセスされたときにのみ実行されます。これにより、値が必要とされない可能性がある場合や、初期化のコストが高い場合のパフォーマンスが向上します。

自分で試してみよう

import 'config_manager.dart';

void main() {
  // TODO: アプリ名 'MyApp' で ConfigManager を作成する
  
  // TODO: 'Config created' を出力する
  
  // TODO: URL 'localhost:5432/mydb' で setupDatabase を呼び出す
  
  // TODO: 'Accessing max connections...' を出力する
  
  // TODO: maxConnections の値を出力する
  
  // TODO: 'Accessing max connections again...' を出力する
  
  // TODO: 再度 maxConnections の値を出力する
  // (計算メッセージが繰り返されないことに注目してください)
  
  // TODO: displayConfig() を呼び出す
}
quiz icon腕試し

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

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