【入門編】Dartの定数式(Constant Expressions)で実行可能な演算の全貌と制限 – Dart コア文法・オブジェクト指向・Null安全解析バイブル

こんにちは!Dartの世界へようこそ。
Flutterを使ったアプリ開発や、バックエンドでのDartプログラミング、楽しんでいますか?

今回は、Dartのコアを支える超重要概念の1つ、「定数式(Constant Expressions)」について深く掘り下げていきます。

「変数を作るなら `var` や `final` で十分じゃないの?」と思うかもしれません。もちろん、それらでもアプリは動きます。しかし、Dartが持つ究極の武器である「コンパイル時定数(`const`)」を完全に使いこなせるようになると、コードの安全性だけでなく、アプリのパフォーマンスやメモリ効率が劇的に跳ね上がります。

「他の言語から来たけれど、Dartの `const` はなんだかルールが厳しくてよく分からない……」
そんなモヤモヤを、ここですべてクリアにしましょう!ここをマスターすれば、あなたのDartの基礎力は確実にプロフェッショナル領域に到達しますよ。

—

1. 変数宣言の御三家:`var`・`final`・`const` の決定的な違い

まずは、Dartの変数宣言を担う3つのキーワードのおさらいから始めましょう。
それぞれの違いを、メモリと時間の軸(コンパイル時 vs 実行時)で捉えるのがコツです。

[コードを書く]
↓
[コンパイル時(ビルド)] ──★ ここで決まるのが const
↓
[実行時(アプリ起動中)] ──★ ここで決まるのが final / var
↓
[メモリ上に展開・代入]

  • `var`(可変): 値を何度でも書き換えられます。型推論が効くので便利ですが、動的な変更が可能です。
  • `final`(実行時定数): 一度だけ値を代入できます。代入されるのはアプリが実行されてそのコードに到達した瞬間です。例えば、現在時刻(`DateTime.now()`)などを入れることができます。
  • `const`(コンパイル時定数): アプリをビルド(コンパイル)する瞬間にはすでに値が完全に確定していなければなりません。コードが実行されるよりはるか昔、開発者の手元でビルドしている最中に計算され、バイナリに埋め込まれます。

脳内イメージ:`const` は「石に刻まれた文字」

`const` で宣言された値は、アプリが起動するよりも前、コンパイルの段階で宇宙の真理のようにカチカチに固められます。だからこそ、後から書き換えることは絶対にできませんし、実行時の計算コストが「ゼロ」になります。

—

2. 定数式(Constant Expressions)で何ができるのか?

「コンパイル時に値を決める」と言っても、ただの数字や文字列を入れるだけではありません。簡単な計算や条件分岐、さらには自作の `const` コンストラクタを使ったオブジェクトの生成まで、定数式の中で行うことができます。

では、実際にどんな演算や処理が「コンパイル時」に許されているのか、その全貌を見ていきましょう。

許可されている演算・操作一覧

1. 基本的な算術演算子: `+`, `-`, “, `~/`(整数割り算), `%`
2. 比較・論理演算子: `==`, `!=`, `<`, `>`, `<=`, `>=`, `&&`, `||`, `!`
3. ビット演算子: `&`, `|`, `^`, `<<`, `>>`
4. 条件(三項)演算子: `condition ? trueVal : falseVal`
5. コレクション(List, Set, Map)のリテラル: ただし、要素もすべて `const` である必要があります。

実例コードで確認してみましょう

// コンパイル時定数を使った安全なコードの例です
const int secondsPerMinute = 60;
const int minutesPerHour = 60;

// 【演算の活用】コンパイル時に計算が行われ、結果(3600)だけがバイナリに残ります
const int secondsPerHour = secondsPerMinute minutesPerHour;

// 【三項演算子の活用】
const bool isDebugMode = true;
const int timeoutLimit = isDebugMode ? 10 : 30;

// 【コレクションの活用】
const List supportedLanguages = [‘Dart’, ‘Flutter’, ‘English’];

これらのコードは、Dartのコンパイラがビルド時にすべて計算・検証し、実行時には「すでに計算された結果」としてメモリ上に配置されます。無駄なCPUサイクルを1ミリ秒たりとも消費しません。

—

3. 【要注意】コンパイル時定数における「厳格な制限」

さて、ここからが本題です。「コンパイル時に値が確定していなければならない」という鉄の掟があるため、実行時にしか分からないものや、副作用(外部の状態変化)を伴うものは、すべてコンパイルエラーになります。

初心者が一番ハマりやすい「やってはいけないこと」を整理しておきましょう。

制限①:実行時関数や動的な値の呼び出し

コンパイル時には、OSの時間や乱数、ユーザーの入力などは一切存在しません。したがって、これらを `const` の中で使うことは不可能です。

// ❌ 外部の状態に依存するもの(コンパイルエラーになります)
const DateTime now = DateTime.now();
// エラー理由: 現在時刻は「アプリを実行した瞬間」に決まるため、コンパイル時には分からない!

// ❌ ランダムな数値
// const int randomValue = Random().nextInt(10);
// エラー理由: 乱数は実行時に生成されるため!

制限②:`final` 変数を `const` の計算に使うことはできない

これも非常によくある勘違いです。

final int userAge = 25; // 実行時に決まる(かもしれない)変数

// ❌ コンパイル時定数の中に final 変数は混ぜられない
// const int doubleAge = userAge 2;
// エラー理由: const は「コンパイル時に絶対動かない不変のデータ」で作る必要があるため、
// 実行時まで値が確定しない final 変数を組み込むことは許されません。

※逆は可能です。`const` の値を使って `final` 変数を作ることは何の問題もありません(例: `final int x = secondsPerHour;` はOK)。

制限③:オブジェクト指向における制限と `const` コンストラクタ

Dartでは、自分で作ったクラスのインスタンスも `const` にすることができます。そのためには、クラス側に `const` コンストラクタ を用意する必要があります。

class Point {
final int x;
final int y;

// コンストラクタに const を付与
const Point(this.x, this.y);
}

void main() {
// コンパイル時にインスタンスが生成される!
const Point origin = Point(0, 0);
}

ここで注意なのは、`const` コンストラクタを持つクラスのプロパティ(上の例の `x` と `y`)は、すべて `final` で宣言されていなければならないというルールです。もし変数(`var`)が混ざっていたら、途中で値が変わる可能性が出てしまい、「コンパイル時に固定する」という大前提が崩れてしまうからです。

—

4. なぜDartはここまで厳しく `const` を縛るのか?(裏側の仕組み)

ここまで読んで、「なんだかルールが多くて窮屈だな……」と感じたかもしれません。
しかし、これにはDart VMおよびFlutterフレームワークのレンダリング戦略において、深遠な理由があります。

Flutterでは、UIの構築(Widgetツリーの構築)が毎秒60回(または120回)という高頻度で行われます。もし、画面が変わらない静的なアイコンやテキストのスタイルを、毎フレームわざわざ新しくメモリ上に生成していたらどうなるでしょう? ガベージコレクター(GC)が頻繁に働き、アプリがカクつく原因(フレームドロップ)になります。

ここで `const` を使ってウィジェットやデータを宣言しておくと、Dartは「一度メモリ上に作ったインスタンスを、アプリが終了するまで完全に使い回す(キャッシュする)」という最適化を自動で行います。

// 毎回新しいインスタンスが作られ、メモリを消費する(非推奨)
var myWidget = Text(‘Hello’);

// アプリ全体でたった1つのインスタンスが共有され、メモリ効率が最大化される(推奨)
const myWidget = Text(‘Hello’);

この「同一性の保証(Canonicalization)」と「メモリの共有」こそが、Dartが定数式に厳格なルールを設けている最大の理由です。コンパイル時に安全性が担保されているからこそ、実行時にはノーコストで安全に使い回すことができるのです。

—

まとめ:今日から使える実践ガイドライン

いかがでしたでしょうか? Dartの定数式と `const` の世界が見えてきましたね。

最後に、開発現場で迷わないためのシンプルなガイドラインを置いておきます。

1. 「アプリのライフサイクル全体を通じて絶対に変わらない値(数学的定数、設定値、固定のUIパーツなど)」は、積極的に `const` にしよう。
2. 迷ったらまず `const` で書けるか試してみよう。 コンパイラがエラーを出してくれたら、それは「まだ値が確定できないタイミングだよ」とDartが優しく教えてくれているサインです。その場合は `final` に切り替えましょう。
3. UIのパフォーマンスを極限まで高めるために、不変なWidgetには必ず `const` をつけよう。

ここをクリアすれば、あなたの書くDartコードの美しさとパフォーマンスはワンランクもツーランクも上がります。ぜひ、今日のコーディングから意識してみてくださいね!

それでは、快適なDart & Flutterライフを!バッチリマスターしていきましょう!

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