! 演算子を使わずに済む!Dart 3 の if case パターン活用法
Dart 3 では新機能として パターンマッチング が導入され、オブジェクトのプロパティをより直感的に取り出せるようになりました。特に if case 構文を活用することで、これまで怖いながらも必要だった ! 演算子を使わずにコードを書けるケースが増えています。
この記事では、Flutter や Dart コードでありがちな「nullable な値の取り扱い」をより安全かつ簡単にする具体的な方法を紹介します。
! 演算子の不安
例えば、ユーザー情報が入った User? user のように nullable な変数を扱う場合、下記のようなコードを書いた経験はありませんか?
dart
コピーする
編集する
if (user?.settings?.theme != null) {
applyTheme(user!.settings!.theme);
}
ロジック的に “絶対に null ではない” と分かっていても、! 演算子で強制的にアンラップするのには抵抗がありますよね。バグが起きたときに例外が発生しやすい部分でもあります。
Dart 3 以前はやむなく使っていた ! ですが、Dart 3 のパターンマッチングのおかげで、この呪縛から解放される手段が増えました。
基本は if case で書く
従来の書き方
dart
コピーする
編集する
void handleUser(User? user) {
if (user?.name != null) {
// 'user' と 'user.name' は null じゃないと判断している
print(user!.name);
}
}
if case を使う書き方
dart
コピーする
編集する
void handleUser(User? user) {
if (user case User(name: final userName)) {
// userName は確実に存在
print(userName);
}
}
if (user case User(name: final userName)) { ... } のおかげで、user が User オブジェクトとして有効である(かつ null ではない)パターンのみ通過するようになります。したがって userName は非 null であることが保証されます。
ネストしたプロパティもスマートに
! への依存をなくしたい場面の多くは、ネストしたオブジェクトが絡んできます。例えば UserPreferences → Theme → ColorScheme のような複数階層で null が混ざる可能性があるときです。
従来の書き方
dart
コピーする
編集する
void updateTheme(UserPreferences? prefs) {
if (prefs?.theme?.colorScheme != null) {
applyColorScheme(prefs!.theme!.colorScheme!);
}
}
if case で階層を解消
dart
コピーする
編集する
void updateTheme(UserPreferences? prefs) {
if (prefs case UserPreferences(theme: Theme(colorScheme: final color))) {
applyColorScheme(color);
}
}
これだけで theme が null かどうか、colorScheme が存在するかどうかを丁寧にチェックしつつ、実際に値が取れればそのまま使えます。コードの見た目もきれいになり、一石二鳥です。
補足: オプションで ? を付けられるパターンもあり、マッチが失敗した場合は if のブロックに入らない仕組みになっています。
こんな場面で if case が便利
複数の変数を同時に取り出したい時
dart
コピーする
編集する
if (user case User(name: final n, age: final a)) {
processUser(n, a);
}
全てが揃っている場合だけコードを実行できるので安心。
特定の値だけが必要な時
dart
コピーする
編集する
if (response case ApiResponse(data: final data)) {
// data を取り出して処理
}
ApiResponse の他のフィールドには興味がない場合も、一気に取り出しできます。
条件付きでフィルタしたい時(when 句)
dart
コピーする
編集する
if (result case Success(value: final v) when v.isNotEmpty) {
showData(v);
}
Success かつ value が空でない場合にだけ実行する、といった使い方が可能。
複雑なパターンは段階的に
あまりにもネストが深いと一行で書くと可読性が落ちます。その場合は、いくつかに分けるのがおすすめです。
dart
コピーする
編集する
void handleNested(ApiResponse? response) {
if (response case ApiResponse(data: final data)) {
// data が null でなければ次へ
if (data case UserData(config: final cfg)) {
// cfg を取り出して次へ
if (cfg case Config(options: final opts)) {
// 取り出した opts を使う
applyOptions(opts);
}
}
}
}
冗長に感じるかもしれませんが、「どの段階で何を取り出しているのか」が一目でわかるので、メンテナンスしやすい書き方になります。
まとめ
Dart 3 のパターンマッチング、特に if case 構文を活用することで、これまで使わざるを得なかった ! 演算子の出番を減らせるようになりました。主なメリットは以下の通りです。
型安全
if case が成功した時点で型が確定するので、! は不要。
ネストが深くてもスッキリ
まとめてチェックと変数の抽出ができる。
意図の明確化
「もしこのパターンに合致するなら……」という形でコードを書くので、可読性が向上。
プロジェクトによってはまだ Dart 3 に移行していないかもしれませんが、早めにこの書き方に慣れておくと、コードがどんどん整理しやすくなるはずです。
参考リンク
Dart公式: Patterns
Reddit などコミュニティの議論 (リンク先を実際の URL に置き換えてご利用ください)
パターンマッチングは if case 以外にも switch 文や cast パターンなど多くの応用があるので、ぜひ公式ドキュメントもチェックしてみてください。みなさんのアプリ開発がより快適になることを祈っています!
Discussion