【入門編】Dartのコレクションリテラルにおける「if」と「for」の活用:宣言的なデータ構築 – Dart コア文法・オブジェクト指向・Null安全解析バイブル

こんにちは!Dartの奥深い世界へようこそ。世界最高峰のDartコアコミッターである私が、今日あなたを「宣言的UIやデータ構築のマスター」へと導きます。

FlutterやDartを使っていると、画面のレイアウトだけでなく、データそのものを組み立てる時にも「あれ、ここでif文やfor文を使いたいけれど、どう書けばスマートだろう?」と悩むことってありませんか?他の言語から来た方だと、わざわざ空のリストを作ってから`if`で要素を`add`したり、面倒な手続き型コードを書きがちですよね。

でも、Dartにはコレクションリテラル内での`if`と`for`(Collection If / Collection For)という、美しく洗練された奥義が備わっています。ここをクリアすれば、あなたのDartコードは一気にモダンで、読みやすく、そして強固なものになりますよ。

さあ、一緒にその本質を見ていきましょう!

—

1. 従来の「泥臭い」コードからの脱却

まずは、他の言語や昔ながらの手法でよく見かける、こんなコードを思い浮かべてみてください。

// 従来の命令型アプローチ
List getMenuItems(bool isLoggedIn) {
List items = [‘ホーム’, ‘設定’];

if (isLoggedIn) {
items.add(‘マイページ’);
items.add(‘ログアウト’);
} else {
items.add(‘ログイン’);
}

return items;
}

このコード、動くには動きますが、「可変(Mutable)なリストを宣言して、後からグニャグニャと要素を付け足す」という構造になっています。これではコードが長くなり、意図が少し見えにくくなってしまいますよね。

Dartの設計思想の根底には「不変性(Immutability)と宣言的構築の愛の深さ」があります。コンパイラやVMにとっても、最初から最終的なリストの形がパッと決まっている方が、最適化の面でも非常に嬉しいのです。

そこで登場するのが、コレクションリテラル内での`if`と`for`です。

—

2. Collection If:条件に応じたスマートな要素の挿入

まずは `if` からいきましょう。リストやマップの中に、直接条件分岐を組み込むことができます。

List getMenuItems(bool isLoggedIn) {
return [
‘ホーム’,
‘設定’,
if (isLoggedIn) …[
‘マイページ’,
‘ログアウト’,
] else …[
‘ログイン’,
],
];
}

おっと、見慣れない記号 `…` が出てきましたね!これはスプレッド演算子(Spread Operator)と呼ばれ、「あるリストの中身を、そのまま別のリストにブチ込む(展開する)」という魔法の構文です。

ここがポイント!

  • `if (isLoggedIn)` の後ろには、単一の要素(例: `’マイページ’`)をそのまま置くこともできます。
  • 複数の要素をまとめて出し入れしたいときは、スプレッド演算子 `…[…]` を使ってリストを展開します。
  • これにより、リストを `final` (または `const`)として宣言したまま、安全に組み立てることが可能になります。

—

3. Collection For:ループによるデータの爆速生成

次に、コレクション内でループを回す `for` の使い方を見てみましょう。例えば、「1から3までの数字をプレフィックス付きでリスト化したい」という場合、こう書けます。

void main() {
int maxCount = 3;

List tags = [
‘一般’,
for (int i = 1; i <= maxCount; i++) 'タグ_$i', ]; print(tags); // 実行結果: [一般, タグ_1, タグ_2, タグ_3] } どうですか?わざわざ外部でループを回して `add` する必要がなく、リストの波括弧 `[]` の中で完結していますよね。データ構造の見た目と、実際のデータの配置が1対1で脳内リンクするため、コードの可読性が劇的に向上します。 もちろん、Map(マップ)の構築時にも `for` や `if` は大活躍します。 void main() { final keys = ['id', 'name', 'role']; final values = [101, 'Alice', 'Admin']; // 2つのリストからMapを宣言的に構築する final Map userMap = {
for (int i = 0; i < keys.length; i++) keys[i]: values[i], 'isActive': true, // 条件付きで追加することも可能 }; print(userMap); // 実行結果: {id: 101, name: Alice, role: Admin, isActive: true} } ---

4. 陥りやすい文法エラーと、その回避の極意

さて、ここで初心者がハマりがちな「罠」をいくつかご紹介しておきましょう。これをあらかじめ知っておくだけで、無駄なコンパイルエラーに悩まされずに済みますよ。

罠その1:セミコロン(`;`)を書いてしまう

通常のDartの文(Statement)では末尾にセミコロンを書くため、うっかりコレクション内でも書いてしまいがちです。

List numbers = [
1,
2,
if (true) 3;, // ❌ エラー!コンパイルエラーになります
];

【解決策】
コレクションリテラルの中は、あくまで「式(Expression)」の世界です。`if` や `for` の中身であっても、文の終わりのセミコロンは不要です。要素と要素の区切りは、あくまでのカンマ(`,`)です。

罠その2:constコンテキストでの動的条件

もしリストを `const`(コンパイル時定数)にしたい場合、実行時に決まる変数や条件分岐は使えません。

const bool isDebug = bool.fromEnvironment(‘DEBUG’);

const List config = [
‘api_endpoint’,
if (isDebug) ‘debug_tools’, // ⭕️ これはOK!isDebugがconstのため
// if (DateTime.now().hour > 12) ‘afternoon’, ❌ 実行時関数はconst内ではNG
];

Dart VMは、`const` で宣言されたコレクションをコンパイル時に完全に評価し、メモリ上のイミュータブルな領域に焼き付けます。そのため、実行時に関数が評価されるような処理を `const` 内の `if` や `for` に混ぜると、怒られてしまいます。

—

5. Dart VMの裏側と、私たちがこの書き方を選ぶ理由

最後に、少しだけアーキテクトらしい話をさせてください。

なぜ、ここまで「宣言的なコレクション構築」を私たちが推すのか?
それは、Dartのコンパイラ(AOTコンパイラ)とJIT(Just-In-Time)VMが、「最初からサイズと構造が確定しているイミュータブルなデータ構造」に対して、最も効率的なメモリ割り当てと最適化を行えるからです。

命令型で `add` を繰り返すコードは、背後で何度もリストのメモリ領域の再割り当て(リサイズ)が発生するリスクを孕んでいます。しかし、コレクションリテラル内で `if` や `for` を使った宣言的コードであれば、コンパイラは「このデータ構造の最終形態はこれだ」と一目で理解し、無駄なオーバーヘッドのない美しいバイトコードを生成できます。

パフォーマンスが高く、バグが入り込む隙がなく、何より人間にとっても読みやすい。――これを使わない手はありませんよね。

—

まとめ

  • Collection If / For を使うことで、リストやマップの構築を宣言的かつ美しく書ける。
  • スプレッド演算子 `…` を組み合わせることで、複数要素の条件付き挿入も自由自在。
  • セミコロンではなくカンマで要素を区切ることに注意する。
  • コンパイル時定数(`const`)と組み合わせることで、Dart VMの最適化恩恵を最大限に受けられる。

ここをクリアすれば、あなたのDartの基礎力は確実に次のステージへとステップアップしています。ぜひ今日からのコードで、スマートなコレクション構築をバンバン試してみてくださいね!それではまた、次回の極限の知見でお会いしましょう。

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