【実務・中級編】Dartのvar, final, constを迷わず使い分けるためのメモリ割り当てとコンパイル時定数の真実 – Dart コア文法・オブジェクト指向・Null安全解析バイブル

Dartのメモリを掌握せよ:var, final, constが生む「静的」と「動的」の境界線

Dartを書く際、`var`、`final`、`const`を何となく使い分けていないだろうか。

「再代入しないなら`final`」「リテラルなら`const`」。教科書通りの回答だが、それだけでは不十分だ。Dart VMの挙動、そしてAOT(Ahead-of-Time)コンパイルの最適化機構を理解すれば、これらは単なる「キーワード」ではなく、メモリ管理と実行時コストを制御する「設計の武器」に変わる。

今日は、Dartの深淵、コンパイルの裏側から、現場で突き抜けたコードを書くための指針を授ける。

—

1. `const`:コンパイル時定数の真実

`const`の本質は「コンパイル時に値が確定し、メモリ上に唯一のインスタンスとして鎮座する」ことにある。

Dartのコンパイラは、`const`で宣言された値(数値、文字列、あるいは`const`コンストラクタを持つクラス)を、プログラムの実行バイナリのデータセグメントに埋め込む。

なぜ`const`を多用すべきなのか

例えば、ウィジェットツリーの中で頻繁に再生成されるUIパーツがあるとする。

// 悪い例:再評価のたびにメモリ確保が発生する
final titleStyle = TextStyle(fontSize: 16);

// 良い例:一度のコンパイルでメモリ上に固定配置される
const titleStyle = TextStyle(fontSize: 16);

`const`を使うことで、その値は実行時に何度もインスタンス化されることはない。メモリ上の特定のアドレスを再利用する(Canonicalization)。Flutterにおいて`const`ウィジェットを多用すべき理由は、単なる微々たる最適化ではなく、不要なObject Allocation(メモリ確保)とGarbage Collection(GC)の負荷を根本から断つためだ。

—

2. `final`:ランタイムの不変性(Immutability)

`final`は「代入は一度きり」を保証する。しかし、`const`とは決定的に異なる。`final`は実行時に値が決まる。

APIから取得したレスポンスを保持するオブジェクト、あるいはコンストラクタ引数で注入される依存関係などは、`const`にはなり得ない。ここで`final`を使うことは、状態の変化を許さないという「設計上の契約」を宣言することと同義だ。

実務での設計指針:データの「流動性」を分離する

非同期API連携を行う際の美しいパターンを紹介しよう。

class UserProfile {
// 実行時に決まる値はfinalにする
final String id;
final String name;

// コンストラクタをconstにすることで、このクラスのインスタンスを
// constとして扱える余地を残す(非常に重要)
const UserProfile({required this.id, required this.name});
}

// APIからの取得データはランタイムで決まるためfinal
final user = await fetchUser();

`final`で宣言された変数は、Dart VM上で最適化の対象となる。コンパイラは「この変数は後から変更されない」ことを知っているため、型推論やインライン展開において強固な根拠を得る。

—

3. `var`:型推論という名の「動的な柔軟性」

`var`は悪ではない。むしろ、スコープが限定された一時変数やループ変数において、過剰な型指定を避けることはコードの可読性を高める。

ただし、「再代入を前提とするか否か」という一点において、`var`を使うべきか、`final`にすべきかを厳格に線引きせよ。

現場のコードレビューで指摘すべきポイント

「なぜこの変数は`var`なのか?」という問いに対し、「後で変わるかもしれないから」という回答は、多くの場合「設計の甘さ」の現れだ。

// 良くない例:後で変わる可能性を放置している
var total = 0;
for (final item in items) {
total += item.price;
}

// 良い例:不変性を担保し、パイプラインで処理する
final total = items.fold(0, (sum, item) => sum + item.price);

`fold`や`map`を用いた関数型アプローチは、一時的な変数を排除し、コンパイラに対して「この変数は不変である」という情報をより強く与えることができる。

—

結論:Dartを使いこなすための「3つの規律」

私が現場でエンジニアに求めているのは、以下の3つのルールだ。

1. デフォルトは `const` とする:
リテラルやUIパーツはまず`const`を検討せよ。メモリを汚さないコードが最も速い。
2. 実行時に確定するなら `final` を徹底する:
状態の変更を許さないことは、バグを未然に防ぐ最高のアーキテクチャだ。
3. `var`は極めて限定的なスコープの一時変数にのみ許す:
`var`が見えたら「なぜ再代入が必要なのか?」を自問せよ。

Dartは現代的な言語の中でも、静的解析と動的実行のバランスを高度にチューニングできるツールだ。あなたが書く一行の変数が、VMのメモリ管理を最適化し、ユーザーの端末で最高のパフォーマンスを発揮する。

その自負を持って、コードを書いてほしい。Dartの深淵は、まだ入り口に立ったばかりだ。

タイトルとURLをコピーしました