こんにちは!FlutterやDartでの開発を楽しんでいますか?
Dartの「Sound Null Safety(健全なNull安全)」は、実行時エラーの温床だった `null` によるクラッシュをコンパイル時に根絶してくれる強力な仕組みですよね。このおかげで、私たちは非常に堅牢なコードを書くことができるようになりました。
でも、ジェネリクス(総称型)と組み合わせたとたん、「あれっ、思った通りの型推論をしてくれない」「なぜここでエラーが出るの?」と頭を悩ませた経験はありませんか?
特に `List
今回は、ジェネリクスにおけるNull許容性の伝播ルールと、型安全なコレクション操作の極意を、優しく丁寧に紐解いていきましょう。
—
1. まずはおさらい:`T` と `T?` の決定的な違い
ジェネリクスの世界へ飛び込む前に、ベースとなる「Null非許容型」と「Null許容型」のおさらいです。
- `T`(Null非許容型): 「絶対に `null` を入れないで!」という強い制約です。
- `T?`(Null許容型): 「値が入るかもしれないし、`null` かもしれないよ」という許容の姿勢です。
これを普通の変数で考えると当たり前のように思えますが、これがクラスやコレクションの型引数(ジェネリクス)になると、途端に挙動が複雑化します。
—
2. `List` と `List` の決定的な違い:箱と中身のルール
ここからが本題です。`List
図解的にイメージしてみましょう。
- `List
` :
「この箱(リスト)自体も `null` にはできないし、中に入っている要素(T)も絶対に `null` にしてはいけない」という厳格なルール。
- `List
` :
「この箱自体は非Nullだけど、中に入っている要素(T?)は `null` を含んでもよい」という柔軟なルール。
実際のコードでその挙動の違いを見てみましょう。
void main() {
// 1. List
List
// ❌ コンパイルエラー!
// strictList.add(null);
// 理由: ‘String’ 型のリストに ‘null’ は追加できません。
// 2. List
List
// ⭕️ これはOK!
flexibleList.add(null);
print(flexibleList); // 出力: [Apple, Banana, null, null]
}
「中身の要素に `null` を混ぜ込みたいなら、型引数に `?` をつけて `List
—
3. 陥りやすい罠:型推論の「思わぬ親切心」とコンパイルエラー
開発現場で本当によくあるのが、「型推論に頼りすぎて、意図しない型になってしまうバグ」です。
次のコードを見てください。あなたなら、この変数 `items` の型は何になると推論されると思いますか?
void main() {
// nullを含んだリストをリテラルで定義してみる
var items = [‘Dart’, ‘Flutter’, null];
// さて、items の型は何でしょうか?
}
正解は、`List
Dartのコンパイラは非常に優秀(親切)なので、リストリテラルの中に `null` が含まれていると、「あ、このリストの要素には `null` が入るんだな」と自動的に察知し、型を `List
では、逆に次のようなケースはどうでしょう?
T processItem
// 何らかの処理をする関数だとします
return item;
}
ここで、`List
void printListItems
for (var item in list) {
// T が非Null型であることが保証されているため、
// itemに対して安全にStringのメソッドなどを呼び出せる
print(item.toString());
}
}
void main() {
List
// ❌ ここでコンパイルエラーになります!
// printListItems(nullableList);
}
なぜエラーになるのか?
コンパイラはこう考えます。
「`printListItems` は `List
このように、「親(リスト)の型引数 `T` が、子(要素)の `null` を許容するかどうか」の厳密な一致が求められるのが、DartのSound Null Safetyの真骨頂です。
—
4. 型安全なコレクション操作のための実践テクニック
「じゃあ、要素に `null` が混ざっているかもしれないリストから、安全に非Nullのデータだけを取り出したい時はどうすればいいの?」という疑問が湧きますよね。
ここで活躍するのが、Dartの強力なコレクションメソッドである `whereType
void main() {
List
// テクニック1: whereType
// これは指定した型(この場合はString)の要素だけを抽出し、
// 自動的に List
List
print(safeList);
// 実行結果: [Dart, Flutter, Null Safety]
// ※ 型は List
}
`whereType
—
まとめ
いかがでしたでしょうか? 今回のポイントをギュッと凝縮して振り返ってみましょう。
1. `List
2. 型推論の罠に注意:リストリテラルに `null` を含めると自動的に `List
3. 関数の引数渡しのミスマッチ:`List
4. `whereType
このジェネリクスとNull安全のルールを頭の片隅に置いておくだけで、コンパイルエラーに直面したときも「あ、今コンパイラは型とNullの安全性を守ろうとしてくれているんだな」とスッと原因が導き出せるようになります。
ここをクリアできれば、Dartの基本はもうバッチリマスターです!自信を持って、さらに高度なアーキテクチャやFlutterの開発に挑んでいきましょう。あなたのコーディングライフがより一層快適になることを応援しています!