こんにちは!FlutterやDartを使った開発を楽しんでいますか?
他の言語、例えばTypeScriptやKotlin、SwiftなどからDartの世界に入ってきた方から、よくこんな質問を受けます。
「あれ、このNull許容型のオブジェクトから、安全にネストしたプロパティを取り出したいんだけど、一番綺麗な書き方はどれ?」
ガード節(`if (obj == null) return;`)を何度も書いたり、無駄に `!`(強制アンラップ)をつけてLinterに怒られたり……心当たりがありませんか?
実は、Dart 3で導入された「パターンマッチング(if-case)」を使いこなすと、このプロパティ抽出とNull安全のハンドリングが驚くほど美しく、そして型安全に決まるんです。
ここをクリアすれば、あなたのDartコードは一気にモダンで洗練されたものになりますよ。一緒にバッチリマスターしていきましょう!
—
1. 従来の「お疲れ様コード」と何が違うのか?
まずは、よく見かける少し冗長なコードを見てみましょう。例えば、「ユーザー情報(User)」の中に「住所(Address)」があり、さらにその中に「都市名(City)」が入っているとします。すべてNull許容型(`?`付き)です。
class Address {
final String? city;
Address(this.city);
}
class User {
final Address? address;
User(this.address);
}
// 従来の書き方(ガード節やネストの嵐になりがち)
void printUserCityOld(User? user) {
if (user == null) return;
final address = user.address;
if (address == null) return;
final city = address.city;
if (city == null) return;
// ここでようやく非Nullのcityが使える
print(‘都市名: $city’);
}
なんだか見通しが悪いですよね。「if文のたびに変数を切り直して……」というのが手間に感じてしまいます。
これをDart 3の `if-case` を使うと、「一撃で分解しながら、Nullではないものだけをキャッチする」という超スマートな書き方に生まれ変わります。
—
2. `if-case` による究極のプロパティ抽出
百聞は一見に如かず。先ほどのコードを `if-case` で書き換えてみましょう。
void printUserCityBestPractice(User? user) {
// if-case とパターンマッチングの組み合わせ
if (user case User(address: Address(city: String cityName?))) {
// このスコープ内では、cityName は「非Nullの String」として保証されている!
print(‘都市名: $cityName’);
} else {
print(‘有効な都市情報がありません’);
}
}
たったこれだけです!何が起きているのか、頭の中でコードがどう評価されるのかを分解して見ていきましょう。
🧠 コードの裏側の動き(Dart VMの視点)
1. `user case` に続くパターン `User(address: Address(city: String cityName?))` は、オブジェクトの構造をそのまま「型抜き」するように検証します。
2. もし `user` が `null` であったり、`address` が `null` であったり、`city` が `null` であった場合、パターンマッチは失敗します。
3. Dartのランタイム(および静的解析器)は、マッチが成功した瞬間に「このパスを通るということは、最深部の `cityName` にたどり着くまでのすべての階層が非Nullである」と確信します。
4. その結果、スコープ内では `cityName` が自動的に非Nullの `String` 型として昇格(Promotion)され、安全に使えるようになります。
—
3. さらにスッキリ!「プロパティ-パターン」の省略テクニック
先ほどのコード、少しだけ冗長に感じませんでしたか? `Address(city: …)` とクラス名を二重に書くのは少し手間に思えますよね。
実はDartのパターンマッチングでは、型が文脈から明らか(あるいは単にプロパティの存在と値だけが欲しい)な場合、コンストラクタ名を省略して直接プロパティを指定できます。
void printUserCityCleaner(User? user) {
// クラス名を省略し、ドットで繋いで直接プロパティを狙い撃ち!
if (user case User(address: 略すことはできませんが、階層はスッキリ書けます)) {
// ※Dartの仕様上、オブジェクトの型名(UserやAddressなど)の指定は必須ですが、
// 変数の型宣言や不要なネストはパターンで綺麗に排除できます。
}
}
実務で最もクリーンで、かつ他の開発者が見ても一目で意図が伝わるベストプラクティスな書き方はこちらです。
void handleUser(User? user) {
// user が存在し、かつ address と city が非Nullのときだけ変数にバインドする
if (user case User(address: Address(city: var city?))) {
// ここで使える city は確実に非Null!
print(city.toUpperCase());
}
}
ここで使われている `var city?`(または具体的な型を書いた `String city?`)という表現に注目してください。
これは「Null-assert パターン」と呼ばれ、「このプロパティが非Nullであること」を条件に含めつつ、変数にその値を代入するというDart 3の強力なシンタックスです。
—
4. 陥りやすい文法エラーと注意点
初学者のうちや、他の言語の感覚で書き進めると、いくつかハマりがちなポイントがあります。ここで先回りしてクリアしておきましょう。
❌ エラー例1: `?` の位置を間違える
// 誤った書き方
if (user case User(address: Address(city: ?String city))) { … }
解説:
Dartのパターンマッチングでは、Nullableであることを示す `?` は、変数の後ろ(あるいは型名の後ろ、かつ変数名の前)に置きます。
- 正解: `String city?` または `var city?`
❌ エラー例2: パターン内でメソッドや計算をしようとする
// 誤った書き方
if (user case User(address: Address(city: String c) when c.length > 0)) { … }
解説:
`if-case` のパターン部分で直接メソッドを呼び出したり条件を入れることはできません。
条件を加えたい場合は、`when` 節(ガード句)を後ろに添えるのが正しいDartの作法です。
// 正しい書き方(when節の活用)
if (user case User(address: Address(city: var city?)) when city.isNotEmpty) {
print(‘有効な都市名: $city’);
}
—
まとめ:今日から使えるベストプラクティス
Dartの `if-case` を使ったNull安全なプロパティ抽出の要点をまとめます。
1. ガード節の連続を卒業する: ネストが深くなる処理は、`if-case` で一網打尽にパターンマッチさせる。
2. `var x?`(Null-assert パターン)を活用する: プロパティが非Nullであることを保証しつつ、スコープ内で安全に変数として利用する。
3. 複雑な条件は `when` 節に逃がす: パターンマッチで構造を綺麗に抜き出し、細かい条件判定は `when` でスマートに記述する。
ここをクリアできれば、あなたの書くDartコードの安全性と美しさはプロフェッショナルそのものになります。ぜひ、今日のコードから取り入れてみてくださいね!