Table of Contents
- 🔹 基本構文
- Switch文(Statement)
- Switch式(Expression)
- 🔹 基本的な使い方
- 1. シンプルなcase文/式
- 2. 型パターンマッチング
- 🔹 when句(ガード条件)
- 基本構文
- 例:オブジェクトパターン(後述)を使った場合分け
- 🔹 パターンマッチング
- 1. リストパターン
- 2. レコードパターン
- 3. オブジェクトパターン
- if文でのパターンマッチングとnullチェック
- 基本的な構文
- 使用例
- 🔹 Switch式 vs Switch文
- ✅ ワイルドカード
_の意味 - 🔹 Switch式における複数条件の指定
- 1. 論理和・論理積(OR/AND)
- 2. 関係演算子パターン - 範囲指定
- 3. 型パターンと条件の組み合わせ
- 4. リストパターンでの複数条件
- 5. マップパターンでの複数条件
- 6. レコードパターンでの複数条件
- 7. オブジェクトパターンでの複数条件
- 8. 複数の変数を組み合わせた条件
- 9. 実用的な複雑な例
- 10. 入れ子になったパターン
- 最後に
- 📚 参考リンク
🔹 基本構文
Switch文(Statement)
switch (値) {
case パターン:
// 処理
case パターン when 条件式:
// ガード条件付き処理
default:
// デフォルト処理
}
※breakは不要
Switch式(Expression)
final result = switch (値) {
パターン => 結果,
パターン when 条件式 => 結果,
_ => デフォルト結果,
};
🔹 基本的な使い方
1. シンプルなcase文/式
// Switch文
switch (color) {
case 'red':
print('赤色です');
case 'blue':
print('青色です');
default:
print('その他の色です');
}
// Switch式
final colorName = switch (color) {
'red' => '赤色',
'blue' => '青色',
_ => 'その他の色',
};
2. 型パターンマッチング
// Switch文
switch (value) {
case int n:
print('整数: $n');
case String s:
print('文字列: $s');
case double d:
print('浮動小数点数: $d');
default:
print('その他の型');
}
// Switch式
final description = switch (value) {
int n => '整数: $n',
String s => '文字列: $s',
double d => '浮動小数点数: $d',
_ => 'その他の型',
};
🔹 when句(ガード条件)
when句を使うことで、パターンマッチに加えて追加の条件を指定できる。
基本構文
case パターン when 条件式:
例:オブジェクトパターン(後述)を使った場合分け
// Switch文
switch (snapshot) {
case AsyncData(:final value) when value.score >= 80:
print('優秀');
case AsyncData(:final value) when value.score >= 60:
print('合格');
case AsyncData(:final value) when value.score >= 0:
print('不合格');
case AsyncData(:final value):
print('無効なスコア');
case AsyncLoading():
print('取得中');
case AsyncError(:final error):
print('スコア取得失敗: $error');
}
// Switch式
final grade = switch (snapshot) {
AsyncData(:final value) when value.score >= 80 => '優秀',
AsyncData(:final value) when value.score >= 60 => '合格',
AsyncData(:final value) when value.score >= 0 => '不合格',
AsyncData(:final value) => '無効なスコア',
AsyncLoading() => '取得中',
AsyncError(:final error) => 'スコア取得失敗: $error',
};
🔹 パターンマッチング
※final 変数名の部分は変数を定義するときと同じ書き方ができ、再代入を許可する場合はvarとすることも可能。
1. リストパターン
switch (data) {
case [int a, int b] when a == b:
print('同じ値の2つの整数');
case [int a, int b]:
print('異なる値の2つの整数: $a と $b');
case [int single]:
print('1つの整数: $single');
case []:
print('空のリスト');
default:
print('その他の構造');
}
2. レコードパターン
final point = (x: 10, y: 20);
final quadrant = switch (point) {
(x: final x, y: final y) when x > 0 && y > 0 => '第1象限',
(x: final x, y: final y) when x < 0 && y > 0 => '第2象限',
(x: final x, y: final y) when x < 0 && y < 0 => '第3象限',
(x: final x, y: final y) when x > 0 && y < 0 => '第4象限',
_ => '原点または軸上',
};
3. オブジェクトパターン
switch (user) {
case User(name: 'admin', age: final age):
print('管理者(年齢: $age)');
case User(name: final name, age: final age) when age < 18:
print('未成年ユーザー: $name');
// スコープ内の変数とオブジェクトのプロパティが同名の場合、省略可
case User(:final name, :final age) when age >= 65:
print('シニアユーザー: $name');
default:
print('一般ユーザー');
}
if文でのパターンマッチングとnullチェック
if文でもcase句を使用することで、同様にパターンマッチングを使用することが出来る。
基本的な構文
if (値 case パターン) {}
if (値 case パターン when 条件) {}
使用例
if (user case SuperUser(:final name)) {
print("こんにちは、$nameさん。");
}
// whenを使用した例
if (user case SuperUser(:final name, :final age) when age >= 30) {
print("こんにちは、$nameさん(30歳以上)。");
}
これは何に使うと便利かというと、単純なnullチェックで昇格できない、オブジェクトのプロパティなどの 非nullを保証できる。
// context.userがnullだった場合、if文の中身は実行されない
if (context.user case final user?) {
print(value.length); // value は String (非null型)
}
// textがnullだった場合、if文に入る前にエラーを投げる
if (context.user case final user!) {
print(value.length); // value は String (非null型)
}
🔹 Switch式 vs Switch文
| 特徴 | Switch式(Expression) | Switch文(Statement) |
|---|---|---|
| 戻り値 | 必ず値を返す | 値を返さない |
| 構文 | 条件 => 結果 |
case 条件: 処理 |
| デフォルト | _ |
default: |
| セミコロン | 必要 | 不要 |
| 用途 | 変数への代入 | 処理の実行 |
// Switch式:値を返す
final result = switch (value) {
1 => 'one',
2 => 'two',
_ => 'other',
}; // セミコロンが必要
// Switch文:処理を実行
switch (value) {
case 1:
print('one');
case 2:
print('two');
default:
print('other');
} // セミコロン不要
✅ ワイルドカード `_` の意味
- Switch式 の中で使用すると・・・すべてにマッチするデフォルトケース
- パターン内 で使用すると・・・値を無視する(変数に代入しない)
final result = switch (value) {
int _ => '何らかの整数', // どの整数でもマッチ、値を使わない
_ => 'その他', // すべてにマッチ
};
🔹 Switch式における複数条件の指定
※上に書いた条件から順に評価される。
1. 論理和・論理積(OR/AND)
// 論理和(OR)
String getDayType(String day) => switch (day) {
'Monday' || 'Tuesday' || 'Wednesday' || 'Thursday' || 'Friday' => '平日',
'Saturday' || 'Sunday' => '週末',
_ => '不明',
};
// 論理積(AND)※単に&&を使った指定も可能
String getDiscount(int age, bool isMember) => switch ((age, isMember)) {
(>= 65, true) => 'シニア会員割引: 30%オフ',
(>= 65, false) => 'シニア割引: 15%オフ',
(< 18, true) => '学生会員割引: 20%オフ',
(_, true) => '会員割引: 10%オフ',
_ => '割引なし',
};
2. 関係演算子パターン - 範囲指定
// 範囲による分類
String getGrade(int score) => switch (score) {
>= 90 => 'A',
>= 80 => 'B',
>= 70 => 'C',
>= 60 => 'D',
>= 0 => 'F',
_ => '無効なスコア',
};
// 複数の関係演算子を組み合わせ
String getAgeGroup(int age) => switch (age) {
< 0 => '無効',
>= 0 && < 13 => '子供',
>= 13 && < 20 => '10代',
>= 20 && < 65 => '成人',
>= 65 => 'シニア',
_ => '不明',
};
3. 型パターンと条件の組み合わせ
// 型と値の条件を組み合わせ
String describe(Object value) => switch (value) {
int n when n > 0 => '正の整数: $n',
int n when n < 0 => '負の整数: $n',
int _ => 'ゼロ',
double d when d > 0.0 => '正の浮動小数点数: $d',
double d when d < 0.0 => '負の浮動小数点数: $d',
double _ => 'ゼロ',
String s when s.isEmpty => '空文字列',
String s => '文字列: $s',
_ => 'その他の型',
};
// null と型チェックの組み合わせ
String handleValue(Object? value) => switch (value) {
// 値がnullの場合
null => 'null値',
// 整数の型チェック + マッチング
int n => '整数値: $n',
// その他の型
double d when d.isFinite => '小数値: $d',
String s => '文字列: $s',
// 上記に該当しないその他すべて
_ => 'その他',
};
4. リストパターンでの複数条件
// リストの要素数と内容で分岐
String analyzeList(List<int> numbers) => switch (numbers) {
[] => '空のリスト',
[_] => '要素1つ',
[final a, final b] when a == b => '同じ値の2要素: $a',
[final a, final b] when a < b => '昇順の2要素: $a, $b',
[final a, final b] => '降順の2要素: $a, $b',
[final first, ..., final last] when first == last => '最初と最後が同じ',
[final first, ... ] when first > 0 => '最初が正の数のリスト',
_ => 'その他のリスト',
};
// 特定のパターンにマッチ
String matchCoordinates(List<num> coords) => switch (coords) {
[0, 0] => '原点',
[final x, 0] => 'X軸上の点: ($x, 0)',
[0, final y] => 'Y軸上の点: (0, $y)',
[final x, final y] when x > 0 && y > 0 => '第1象限',
[final x, final y] when x < 0 && y > 0 => '第2象限',
[final x, final y] when x < 0 && y < 0 => '第3象限',
[final x, final y] when x > 0 && y < 0 => '第4象限',
_ => 'その他',
};
5. マップパターンでの複数条件
// マップのキーと値で複数条件
String processRequest(Map<String, dynamic> request) => switch (request) {
{'method': 'GET', 'path': '/api/users'} => 'ユーザー一覧を取得',
{'method': 'GET', 'path': String path} when path.startsWith('/api/users/') =>
'ユーザー詳細を取得',
{'method': 'POST', 'path': '/api/users', 'body': Map body} =>
'ユーザーを作成',
{'method': 'PUT', 'path': String path, 'body': Map body}
when path.startsWith('/api/users/') => 'ユーザーを更新',
{'method': 'DELETE', 'path': String path}
when path.startsWith('/api/users/') => 'ユーザーを削除',
{'method': String method} => '不明なエンドポイント: $method',
_ => '無効なリクエスト',
};
// 複数のキーの組み合わせで判定
String getUserStatus(Map<String, dynamic> user) => switch (user) {
{'isActive': true, 'isPremium': true, 'role': 'admin'} =>
'プレミアム管理者',
{'isActive': true, 'isPremium': true} =>
'プレミアムユーザー',
{'isActive': true, 'role': 'admin'} =>
'管理者',
{'isActive': true} =>
'通常ユーザー',
{'isActive': false} =>
'無効なユーザー',
_ =>
'不明',
};
6. レコードパターンでの複数条件
// レコードの値で複数条件
String analyzePoint((int x, int y) point) => switch (point) {
(0, 0) => '原点',
(final x, 0) => 'X軸上',
(0, final y) => 'Y軸上',
(final x, final y) when x == y => '対角線上',
(final x, final y) when x == -y => '逆対角線上',
(final x, final y) when x > 0 && y > 0 => '第1象限',
(final x, final y) when x < 0 && y > 0 => '第2象限',
(final x, final y) when x < 0 && y < 0 => '第3象限',
(final x, final y) when x > 0 && y < 0 => '第4象限',
_ => 'その他',
};
// 名前付きレコード
String analyzeUser((String name, int age, {bool isPremium}) user) =>
switch (user) {
(name: 'admin', age: _, isPremium: _) => '管理者アカウント',
(name: _, age: < 18, isPremium: _) => '未成年ユーザー',
(name: _, age: >= 65, isPremium: true) => 'シニアプレミアムユーザー',
(name: _, age: _, isPremium: true) => 'プレミアムユーザー',
(name: final name, age: final age, isPremium: false) =>
'通常ユーザー: $name ($age歳)',
_ => '不明',
};
7. オブジェクトパターンでの複数条件
sealed class Shape {}
class Circle extends Shape {
final double radius;
Circle(this.radius);
}
class Rectangle extends Shape {
final double width, height;
Rectangle(this. width, this.height);
}
class Triangle extends Shape {
final double base, height;
Triangle(this.base, this. height);
}
// オブジェクトの型とプロパティで複数条件
double calculateArea(Shape shape) => switch (shape) {
Circle(radius: final r) when r <= 0 => 0,
Circle(radius: final r) => 3.14 * r * r,
Rectangle(width: final w, height: final h) when w <= 0 || h <= 0 => 0,
Rectangle(width: final w, height: final h) => w * h,
Triangle(base: final b, height: final h) when b <= 0 || h <= 0 => 0,
Triangle(base: final b, height: final h) => b * h / 2,
};
// 複雑な条件
String describeShape(Shape shape) => switch (shape) {
Circle(radius: final r) when r > 10 => '大きな円',
Circle(radius: final r) when r > 5 => '中くらいの円',
Circle(radius: final r) when r > 0 => '小さな円',
Rectangle(width: final w, height: final h) when w == h => '正方形',
Rectangle(width: final w, height: final h) when w > h => '横長の長方形',
Rectangle(width: final w, height: final h) when w < h => '縦長の長方形',
Triangle(base: final b, height: final h) when b == h => '底辺と高さが同じ三角形',
_ => 'その他の図形',
};
8. 複数の変数を組み合わせた条件
// タプル(レコード)を使った複数変数の判定
String checkCredentials(String username, String password) =>
switch ((username, password)) {
('', _) => 'ユーザー名が空です',
(_, '') => 'パスワードが空です',
('admin', 'admin123') => '管理者としてログイン',
(final user, final pass) when user. length < 3 =>
'ユーザー名が短すぎます',
(final user, final pass) when pass.length < 8 =>
'パスワードが短すぎます',
(final user, _) when user.contains('@') =>
'メールアドレスでログイン',
_ => '通常ログイン',
};
// 複数パラメータの組み合わせ
String determineShipping(double weight, String destination, bool express) =>
switch ((weight, destination, express)) {
(_, _, true) when weight > 30 => '特急便(重量物)',
(_, _, true) => '特急便',
(> 30, 'domestic', false) => '国内便(重量物)',
(<= 30, 'domestic', false) => '国内便(通常)',
(> 30, 'international', false) => '国際便(重量物)',
(<= 30, 'international', false) => '国際便(通常)',
_ => '配送方法を確認してください',
};
9. 実用的な複雑な例
// HTTPレスポンスの処理
String handleResponse(int statusCode, Map<String, dynamic>? body) =>
switch ((statusCode, body)) {
(200, {'data': List data}) when data.isNotEmpty =>
'成功: ${data.length}件のデータ',
(200, {'data': List data}) when data.isEmpty =>
'成功: データなし',
(201, {'id': int id}) =>
'作成成功: ID $id',
(400, {'error': String msg}) =>
'リクエストエラー: $msg',
(401, _) =>
'認証エラー',
(403, _) =>
'権限エラー',
(404, _) =>
'見つかりません',
(>= 500, _) =>
'サーバーエラー',
_ =>
'不明なレスポンス',
};
// ファイル処理の判定
String getFileAction(String extension, int size, bool isReadOnly) =>
switch ((extension. toLowerCase(), size, isReadOnly)) {
(_, _, true) => 'ファイルは読み取り専用です',
('exe' || 'bat' || 'sh', _, _) => '実行ファイルです',
('jpg' || 'png' || 'gif', > 10000000, _) =>
'大きな画像ファイル(${(size / 1000000).toStringAsFixed(1)}MB)',
('jpg' || 'png' || 'gif', _, _) =>
'画像ファイル',
('mp4' || 'avi' || 'mov', _, _) =>
'動画ファイル',
('txt' || 'md', < 1000, _) =>
'小さなテキストファイル',
('txt' || 'md', _, _) =>
'テキストファイル',
(_, > 100000000, _) =>
'非常に大きなファイル(${(size / 1000000).toStringAsFixed(1)}MB)',
_ =>
'一般ファイル(.$extension)',
};
10. 入れ子になったパターン
// 複雑な入れ子構造
String analyzeData(dynamic data) => switch (data) {
// リストの中にマップがある場合
[{'type': 'user', 'name': String name}] =>
'単一ユーザー: $name',
// 複数要素でパターンマッチ
[{'type': 'user', 'name': String name}, ... ] when data.length > 5 =>
'多数のユーザー(${data.length}人)',
// マップの中にリストがある場合
{'users': List users, 'total': int total} when users.length == total =>
'ユーザーリスト完全: $total人',
{'users': List users, 'total': int total} when users.length < total =>
'ユーザーリスト部分的: ${users.length}/$total人',
// 深い入れ子
{'data': {'user': {'name': String name, 'age': int age}}}
when age >= 18 => '成人ユーザー: $name',
_ => 'その他のデータ構造',
};
最後に
このチートシートは、Dart 3以降のモダンなswitch構文に対応している。独特な構文で慣れが必要だが、パターンマッチングとガード条件(when句)を活用することで、より表現力豊かで読みやすいコードが書けるようになるはず。
📚 参考リンク
- Dart公式ドキュメント:Branches
- Dart Switch Expression Syntax and Examples - Bugsee
- Dart Switch Expressions - ChristianFindlay.com
※本記事は、GitHub Copilot「Claude Sonnet 4.5」を利用して出力したものを加筆修正したものです。