【入門編】Null許容型と『List.cast()』の危険な関係:実行時例外を回避する型安全な変換術 – Dart コア文法・オブジェクト指向・Null安全解析バイブル

こんにちは。Dartの深淵へようこそ。

Dartの「Sound Null Safety」は、単なる文法上のルールではありません。コンパイル時という「静的な世界」と、実行時という「動的な世界」の間に架けられた、極めて厳格な契約です。

特に、`List.cast()`というメソッドは、この契約を危うくする「爆弾」を抱えていることがあります。今日は、多くの開発者が遭遇する「なぜか動かない」「なぜか落ちる」という悪夢を、Dart VMの内部挙動を意識しながら解き明かしていきましょう。

—

1. `List.cast()` の正体:それは「型アサーション」に過ぎない

まず、結論から言います。`List.cast()` は、リストの中身を一つずつ変換しているわけではありません。

これは、コンパイラに対して「このリストは、これから `T` 型のリストとして扱うからよろしく頼むよ」と主張する、いわば「型アサーション(強制的な型宣言)」です。

List rawList = [1, 2, null];

// ここでキャストを実行
// 実行時、Dart VMは「本当にListか?」を詳細にチェックする余裕がない
List intList = rawList.cast();

// 使う瞬間に悲劇が起こる
print(intList[2]); // 実行時エラー! (type ‘Null’ is not a subtype of type ‘int’)

なぜこうなるのか? `cast()` は、リストの「ビュー(見え方)」を変えるだけで、中身の精査は行わないからです。アクセスした瞬間に、Dartのランタイムが「おっと、期待していた `int` じゃないぞ!」と検出し、例外を投げます。これが、Null許容型が混入した際の最も典型的なクラッシュです。

—

2. なぜ私たちは「実行時例外」に追い込まれるのか?

初心者が陥りやすいのは、APIから受け取った `List` や `List` を、「とりあえずキャストしておけばいいや」と安易に扱うケースです。

特に、JSONパースなどの「外部からの入力」は、Nullが混入している可能性を常に考慮しなければなりません。

陥りやすいパターン:

// APIから来たリスト
List data = [10, 20, null, 40];

// 「Intのリストのはずだ!」と信じてキャスト
final validList = data.cast();

// 画面描画ロジックなどでループを回すと…
for (final item in validList) {
print(item + 1); // ここで ‘null’ が入り込み、即座にクラッシュ
}

このコードは、コンパイル時には何の警告も出しません。しかし、本番環境でユーザーの端末をクラッシュさせるトリガーになります。

—

3. 安全な変換術:『フィルタリング』という防波堤

では、どうすれば安全に変換できるのでしょうか。答えはシンプルです。「キャスト」するのではなく、「再構築」することです。

Dartのイディオムを活用し、Nullを排除した新しいリストを作り出しましょう。

推奨されるアプローチ:`whereType()`

Dartの `Iterable` には、型安全にリストをフィルタリングする強力な武器が用意されています。

List rawList = [10, 20, null, 40];

// whereType() は、型が int であるものだけを抽出し、
// 自動的に List として再構築してくれます。
List safeList = rawList.whereType().toList();

print(safeList); // [10, 20, 40] -> Nullが消えて安全に!

`whereType()` を使うことで、Dart VMは「型が一致しないものは捨て、一致するものだけで新しいリストを生成する」という処理を行います。これは「キャスト」よりも計算コストはかかりますが、「実行時例外でアプリが落ちる」という最大のリスクを完全に封じ込めることができます。

—

4. プロの現場で意識すべきこと

もし、あなたが「どうしてもNullを保持したまま変換したい」のであれば、それは `List` ではなく `List` であるべきです。

  • `cast()` は、中身が100% `T` であると確信が持てる時だけ使う(主にライブラリ内部などの限定的な場所)。
  • `whereType()` は、不確定なデータソースから安全に特定の型を取り出す時に使う。
  • `map()` + `where()` は、変換とフィルタリングを同時に行いたい時に使う。

まとめ:Dartを掌握するために

Dartの型システムは、あなたの敵ではなく「最大の味方」です。`cast()` を使う前に、「本当にこれは中身を触らずに型だけを変えても安全か?」と自問自答してみてください。

ここをクリアすれば、あなたの書くコードは「動く」だけでなく「壊れない」ものへと進化します。DartのNull安全という契約を理解し、適切に型を制御することで、より高次元な開発者へとステップアップしていきましょう。

もし迷ったら、まずは `whereType` を選ぶ。これだけで、あなたのアプリの安定性は劇的に向上しますよ。さあ、安全で堅牢なDartライフを楽しんでください!

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