【入門編】Dartの関数は第一級オブジェクトである:クロージャを活用した状態保持の仕組み – Dart コア文法・オブジェクト指向・Null安全解析バイブル

こんにちは。Dartの世界へようこそ。
Dartは一見するとJavaやC#に近い「堅実なオブジェクト指向言語」に見えますが、その深淵にはLispやJavaScriptから受け継いだ「関数型プログラミング」の強力なDNAが流れています。

今回は、Dartを使いこなすための登竜門であり、かつ最強の武器の一つである「クロージャ(Closure)」についてお話しします。クラスを使わずに「状態」を保持する、このスマートなテクニックをマスターしましょう。

—

1. 関数は「ただの処理」ではない:第一級オブジェクトという概念

Dartにおいて、関数は「第一級オブジェクト」です。これはどういうことかと言うと、関数を変数に代入したり、他の関数の引数として渡したり、関数の戻り値として関数を返したりできるということです。

void main() {
// 関数を変数に代入できる
var sayHello = (String name) => print(‘こんにちは、$nameさん!’);

sayHello(‘Dart使い’); // 実行結果: こんにちは、Dart使いさん!
}

この「関数を返す」という性質こそが、クロージャを生み出す魔法の鍵になります。

—

2. クロージャ:関数が「記憶」を持ち歩く仕組み

クロージャとは、「自分が生まれた場所(スコープ)にある変数を、外に持ち出した後も参照し続けられる関数」のことです。

クラスを使わずにカウンターを作ってみましょう。

// カウンターを作る「工場」のような関数
Function createCounter() {
int count = 0; // この変数が「状態」になります

return () {
count++; // 外側のスコープにあるcountを書き換える
print(‘現在のカウント: $count’);
};
}

void main() {
final counter = createCounter();

counter(); // 1
counter(); // 2
counter(); // 3
}

なぜこれが動くのか?

通常、`createCounter`関数が終了した時点で、ローカル変数 `count` はメモリから解放されるはずですよね?
しかし、DartのVM(仮想マシン)は、「内部の無名関数がまだ `count` を必要としている」ことを検知し、その変数を生存させ続けます。これがクロージャの本質です。関数が「スコープの境界を超えて変数を抱きしめている」イメージです。

—

3. クラス vs クロージャ:使い分けの指針

「それなら何でもクロージャでいいのでは?」と思うかもしれません。しかし、ここがDartの深みです。

  • クラスを使うべき時:
  • 状態が複数ある(名前、ID、設定値など)。
  • メソッドを外部から呼び出したり、型として明示的に扱いたい。
  • コードの構造をカプセル化して保守性を高めたい。
  • クロージャを使うべき時:
  • 「カウンター」や「単一のアクセストークン」のように、隠蔽したい状態が一つか二つで十分なとき。
  • コードを簡潔に保ちたいとき。

—

4. 初学者が陥りやすい「罠」

クロージャを書く際に、よくやってしまうミスがあります。

罠:ループ内でのクロージャ生成

void main() {
var functions = [];
for (var i = 0; i < 3; i++) { // 悪い例:iがループの終了条件に引きずられることがある functions.add(() => print(i));
}

for (var f in functions) {
f(); // 期待値は 0, 1, 2 だが、古い言語仕様ではすべて 3 になることも
}
}

現代のDartでは `for` ループの変数はイテレーションごとにスコープが作られるため安全ですが、古いJavaScriptの知識がある方は注意が必要です。また、「巨大なオブジェクトをクロージャがキャプチャし続けると、メモリリークの原因になる」という点も、メモリを意識するDartエンジニアとしては覚えておいてください。

—

まとめ:ここをクリアすれば、Dartはもっと自由になる

クロージャを活用できるようになると、あなたのコードは以下のように変わります。

1. ボイラープレート(定型文)が減る: 小さな状態管理のためにわざわざクラスを書く必要がなくなる。
2. 高階関数を使いこなせる: `map`, `where`, `fold` といったDartの強力なリスト操作関数は、すべてこのクロージャの仕組みの上で動いています。
3. Flutter開発が加速する: FlutterのUI構築は、クロージャの塊です。`onPressed: () { … }` という記述も、まさにクロージャそのもの。

Dartは、型安全という「規律」と、クロージャという「柔軟性」が絶妙なバランスで共存している言語です。関数を「ただの処理」ではなく「状態を抱えた生き物」として扱えるようになれば、あなたはもうDartの基本を完全に掌握したと言っても過言ではありません。

さあ、次はどんな機能を組み合わせてコードを書いてみましょうか?あなたの挑戦を応援していますよ!

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