Contents
三項演算子の基本と構文
Java の三項演算子(条件演算子)は、condition ? expr1 : expr2 という形で記述します。左側の condition が true になると expr1 が評価され、その結果が式全体の値となります。false の場合は expr2 が評価されます。この構文は「式」なので、代入文やメソッド呼び出しの引数に直接埋め込むことができます。
|
1 2 3 4 |
int a = 7, b = 5; int max = (a > b) ? a : b; // a が大きければ a、そうでなければ b を代入 String status = (age >= 20) ? "成人" : "未成年"; |
if 文との比較:式 vs. 文
if 文は制御フローを表す「文」であり、複数行にわたる処理を書けます。一方三項演算子は単一の式として結果だけを返すため、代入や戻り値の決定というシンプルなケースで有効です。どちらを選ぶべきかは「可読性」と「副作用」の有無が判断基準になります。
機能的な違い
- if 文:ブロック内に複数のステートメントを書ける。
break・continueも利用可能。 - 三項演算子:式として扱われ、結果を即座に取得できる。副作用を伴う処理は避けた方が安全。
可読性と保守性の観点
条件が単純であれば一行で完結させられるためコードがすっきりしますが、条件式や分岐結果が長くなると逆に読みにくくなります。実務では「1 行で表現できるか」を基準にし、複雑になる場合は if 文またはメソッド抽出を検討します。
演算子優先順位と安全な書き方
三項演算子の優先順位は 代入演算子 (=) より高く、論理 OR (||) やビット OR (|) より低い 位置にあります。そのため他の演算子と組み合わせる際にはカッコで明示しないと意図しない評価順になることがあります。
|
1 2 3 4 5 6 7 8 9 10 11 |
int a = 3, b = 4, c = 5; // 正しい書き方(条件式全体を括弧) int result1 = (a > b) ? a + c : b - c; // 誤解しやすい例:* が先に評価される可能性 int result2 = a > b ? a + c : b - c * 2; // 実際は b - (c * 2) が計算される // カッコで明示すると意図が分かりやすくなる int safe = (a > b) ? (a + c) : (b - c); |
ポイント
- 三項演算子は代入より高い優先順位なので、
x = condition ? a : b;のように直接代入できる。 &&・||と組み合わせるときは必ず条件全体を括弧で囲むと安全です。
型推論と null 安全性の具体例
三項演算子では 左右の式の型が統一されていなければコンパイルエラー になります。型推論がうまく働くケースと、明示的にキャストや Optional が必要になるケースを見てみましょう。
型が自動で揃う例
|
1 2 3 4 5 6 |
// int と Integer は自動ボクシング/アンボクシングで統一される int x = flag ? 10 : new Integer(20); // String と null の場合、null が String 型として推論される String msg = (user != null) ? user.getName() : null; |
型不一致でコンパイルエラーになる例
|
1 2 3 4 5 6 7 |
// String と int は型が合わないのでエラー Object o = flag ? "OK" : 0; // コンパイルエラー // ジェネリック型の違いも問題になる List<String> list = condition ? Arrays.asList("a") : Collections.emptyList(); // OK List<Integer> intList = condition ? Arrays.asList(1) : Collections.emptyList(); // エラー |
対処法
-
キャストで明示
java
String s = flag ? "OK" : (String) null;
Object o = flag ? "OK" : (Object) 0; // どちらも Object に統一 -
Optional を活用(null 回避)
java
Optional<String> optName = Optional.ofNullable(user)
.map(User::getNickname);
String display = optName.orElse(flag ? "ゲスト" : "匿名");
null 判定の落とし穴
三項演算子内部で null を左側に置くと、評価順序によっては NullPointerException が発生します。安全な書き方は必ず先に null チェックを行うことです。
|
1 2 |
String label = (obj != null && obj.isValid()) ? obj.getName() : "default"; |
実務での活用パターンとベストプラクティス
実際の開発現場では、三項演算子を 代入・戻り値の決定、Stream との組み合わせ、Optional とラムダ式での利用が頻繁に見られます。以下に代表的なパターンと推奨される書き方をまとめました。
1. 代入やメソッド戻り値への簡潔利用
|
1 2 3 |
String role = user.isAdmin() ? "ADMIN" : "USER"; return order.isPaid() ? OrderStatus.COMPLETED : OrderStatus.PENDING; |
- 条件がシンプルで副作用が無いときに限る。
- 複数行になる場合は
if文へリファクタリング。
2. Stream 内での条件分岐
|
1 2 3 4 |
List<String> labels = items.stream() .map(item -> item.isActive() ? "ACTIVE" : "INACTIVE") .collect(Collectors.toList()); |
mapのラムダ式は式として書けるので、三項演算子が自然にマッチする。
3. Optional とラムダ式の併用例
|
1 2 3 4 |
String displayName = optionalUser .map(User::getNickname) .orElseGet(() -> isGuest ? "ゲスト" : "匿名"); |
OptionalのorElseGetに渡す Supplier でも三項演算子が有効。nullを直接扱わずに済むので NPE のリスクが低減する。
4. ネストした三項演算子の回避
条件が 3 つ以上 必要になるケースでは、ネストすると可読性が急激に低下します。代替案として if‑else 文やヘルパーメソッドを使うと良いでしょう。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// 悪い例(ネスト) String grade = score >= 90 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : "D"; // 推奨例(メソッド抽出) String grade = evaluateGrade(score); private String evaluateGrade(int s) { if (s >= 90) return "A"; if (s >= 80) return "B"; if (s >= 70) return "C"; return "D"; } |
ベストプラクティスまとめ
- 条件は 1 行で完結させ、長くなる場合はメソッドに切り出す。
Stream・Optionalと組み合わせると関数型スタイルでも可読性が保たれる。- ネストは 2〜3 条件までに留め、それ以上は
if‑elseまたはヘルパーメソッドで置き換える。
コードレビューでのチェックリスト
コードレビュー時に三項演算子を見つけたら、以下の項目を確認してください。表の左列がチェック項目、右列が具体的な確認内容です。
| チェック項目 | 確認内容 |
|---|---|
| 可読性 | 条件式はシンプルか?ネストや長い分岐がないか |
| 型の統一 | 左右の式が同じ型、または明示的にキャストされているか |
| null 安全性 | null 判定が正しく行われ、NPE の危険がないか |
| 副作用の有無 | 分岐内で状態変更や例外発生を伴うメソッド呼び出しが混在していないか |
| カッコ付け | 演算子優先順位による誤解を防ぐため、必要な箇所に括弧があるか |
このチェックリストを活用すれば、三項演算子の乱用やバグ潜在的リスクを早期に発見でき、コードベースの品質向上につながります。
まとめ
- 三項演算子は 式として値を返す 便利な構文だが、優先順位・型一致・null 安全性に注意が必要。
if文との使い分けは「シンプルさ」と「副作用の有無」がポイントになる。- 実務では代入や
Stream/Optionalと組み合わせた 簡潔な記述 が主流だが、条件が増えるとメソッド抽出へリファクタリングすべき。 - コードレビュー時のチェックリストを活用し、可読性・安全性を保った実装を目指しましょう。