Contents
1. 三項演算子の基本構文と評価順序
1‑1. 基本形
|
1 2 |
result = condition ? exprWhenTrue : exprWhenFalse; |
| 要素 | 説明 |
|---|---|
| condition | boolean を返す式。必ず左から右へ評価される。 |
| exprWhenTrue | condition が true のときだけ評価され、結果が演算子全体の戻り値になる。 |
| exprWhenFalse | condition が false のときだけ評価され、結果が演算子全体の戻り値になる。 |
1‑2. 評価順序
- condition を左から右へ評価
- 条件の真偽に応じて exprWhenTrue または exprWhenFalse のどちらか一方だけを評価
- 評価された式の結果が三項演算子全体の値になる
重要ポイント
-condition、exprWhenTrue、exprWhenFalseのいずれも副作用(メソッド呼び出しや代入)を持つことができるが、選択されなかった側は 実行されない。これにより不要な計算や副作用の発生を防げる。
例 1 – 基本的な数値演算
|
1 2 3 |
int a = 12; int b = (a > 5) ? a * 2 : a / 2; // a が 5 超なら 24、そうでなければ 6 |
a > 5→truea * 2が評価され、bに代入される。a / 2は評価されないので計算コストがかからない。
例 2 – 副作用のあるメソッド呼び出し
|
1 2 3 |
String msg = (logger.isDebugEnabled()) ? logger.debug("debug mode") : "no debug"; |
- デバッグが有効なときだけ
logger.debugが実行され、無効なら文字列リテラルがそのまま代入される。 - 無駄なログ出力を防げる典型的なパターンです。
2. if‑else 文との比較と適用シーン
| 観点 | 三項演算子 | if‑else 文 |
|---|---|---|
| 記述量 | 1 行で完結できる(式がシンプルな場合) | 必要最低でも 3 行以上になることが多い |
| 可読性 | 条件と結果が一目で分かる → シンプルなロジックに最適 | 複数ステートメントや副作用が混在すると見通しが良くなる |
| 戻り値の利用 | 式として直接 return や代入に組み込める |
if‑else でも可能だがブロックが増える |
| デバッグ | 条件部だけをステップ実行すれば OK | ブロック単位で止まれるので細かい追跡が楽 |
2‑1. 三項演算子が適切なケース
- 変数初期化・代入
java
int max = (a > b) ? a : b; - メソッドの戻り値
java
return (list.isEmpty()) ? Collections.emptyList() : list; - 文字列リテラルの切替
java
String mode = debug ? "DEBUG" : "PROD";
2‑2. if‑else が望ましいケース
| ケース | 理由 |
|---|---|
| 複数ステートメント(代入+ロギング等) | 1 行に詰め込むと可読性が低下する |
| 例外を投げるメソッド呼び出しが混在 | 三項演算子は式なので throw を直接書けない |
| 条件分岐が 3 層以上になるネスト | インデントや改行で可読性を保ちにくい |
例 – if‑else が適切なパターン
|
1 2 3 4 5 6 7 8 |
if (isValid) { logger.info("valid"); status = Status.OK; } else { logger.warn("invalid"); status = Status.ERROR; } |
3. 実務で活用できる基本・応用例
3‑1. 数値比較と文字列選択
|
1 2 3 4 5 |
int discount = (price > 1000) ? 200 : 0; String grade = (score >= 90) ? "A" : (score >= 80) ? "B" : (score >= 70) ? "C" : "D"; |
3‑2. ブールフラグの簡易設定
|
1 2 |
boolean isAdult = age >= 18; |
3‑3. null チェックと Optional の併用
|
1 2 3 4 5 6 7 |
String name = (user != null && user.getName() != null) ? user.getName() : "Anonymous"; Optional<String> opt = Optional.ofNullable(input); String result = opt.map(String::trim).orElse("default"); |
3‑4. Stream の中間操作での利用
|
1 2 3 4 |
List<String> labels = items.stream() .map(i -> i.isActive() ? "ACTIVE" : "INACTIVE") .collect(Collectors.toList()); |
4. ネストと可読性、型に関する注意点
4‑1. ネストした三項演算子の書き方
|
1 2 3 4 5 |
String level = (score >= 90) ? "S" : (score >= 80) ? "A" : (score >= 70) ? "B" : (score >= 60) ? "C" : "D"; |
- インデントと改行で階層を明示し、1 行あたりの文字数は 80 文字以内に抑える。
- 複雑さが増す場合は
if‑elseに切り替えるか、ヘルパーメソッドへ分割する。
4‑2. 型推論とボクシング
| パターン | 結果 |
|---|---|
Integer i = flag ? 1 : null; |
OK(int → Integer の自動ボクシング) |
int j = flag ? 1 : null; |
コンパイルエラー(null をプリミティブに代入不可) |
Number n = flag ? 3.14 : 2; |
OK(double と int が共通上位型 Number に統一) |
対策
- null が返る可能性がある場合はラッパークラスか Optional<T> を使用する。
- プリミティブとラッパーが混在すると暗黙のボクシングが発生し、意図しないオートボックス例外(NullPointerException)を招くことがあるので注意。
5. エラー対処・パフォーマンス・コーディング規約
5‑1. よくあるコンパイルエラーと回避策
| エラーパターン | 原因 | 修正例 |
|---|---|---|
int x = cond ? 1 : "a"; |
型不一致(int と String) |
Object x = cond ? Integer.valueOf(1) : "a"; |
boolean b = cond ? true : null; |
null は boolean に代入できない |
Boolean b = cond ? Boolean.TRUE : null; |
var r = cond ? methodA() : methodB(); // 戻り値型が異なる |
両メソッドの戻り値型が共通でない | 共通インタフェース/スーパークラスにキャスト、または別々に代入する |
5‑2. パフォーマンス比較(ベンチマーク概要)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// if‑else バージョン long tIf = System.nanoTime(); for (int i = 0; i < 10_000_000; i++) { int v = (i % 2 == 0) ? i : -i; } long dIf = System.nanoTime() - tIf; // 三項演算子バージョン(同等ロジック) long tTernary = System.nanoTime(); for (int i = 0; i < 10_000_000; i++) { int v = (i % 2 == 0) ? i : -i; } long dTernary = System.nanoTime() - tTernary; System.out.printf("if‑else: %d ns, ternary: %d ns%n", dIf, dTernary); |
- 実測結果は数十ナノ秒程度の差に留まる。
- JIT が同一バイトコードを生成するため、実務上の速度差は無視できる。
5‑3. 主なコーディング規約と推奨設定
| 規約 | 推奨条件 | 非推奨例 |
|---|---|---|
| Google Java Style | 1 行 ≤ 80 文字、式がシンプル(condition ? a : b) |
ネストが 2 層以上、または行数が 3 行を超える三項演算子 |
| Checkstyle – TernaryOperator | maxLineLength と組み合わせて警告抑制 |
1 行で 120 文字を超える長い三項式 |
| SonarJava | 「Complexity」指標が上がる場合は if‑else に置換 |
条件分岐が 3 つ以上のネストで可読性が低下 |
実務的アドバイス
- シンプルさ を最優先し、1 行に収まるかどうかで採用判断を行う。
- 長くなる場合は必ずコメントやヘルパーメソッドで意図を明示する。
6. まとめ
| 項目 | 内容 |
|---|---|
| 基本構文 | condition ? exprWhenTrue : exprWhenFalse。条件が true のときだけ左側、false のときだけ右側が評価される。 |
| if‑else との使い分け | 代入・返却のみ のシンプルケースは三項演算子でコード量削減。 複数ステートメントや副作用 がある場合は if‑else を選択し、可読性を確保する。 |
| 実務応用 | 数値・文字列の切替、null 安全化、Optional、Stream の中間操作など多様な場面で活躍。 |
| ネストと型注意点 | インデントと改行で階層を見やすくし、プリミティブとラッパーの混在に伴うボクシング・null 代入エラーに留意する。 |
| エラー対策・規約 | 型不一致は両側を同一型へ統一、長い式や多層ネストは非推奨。Google Java Style や Checkstyle の設定と合わせて使用すればコード品質が向上する。 |
| パフォーマンス | if‑else とほぼ同等。可読性・保守性の観点で選択すべき。 |
最終的な指針
- 「式がシンプルで 1 行に収まる」 → 三項演算子を採用
- 「処理が増える、または可読性が低下しそう」 → if‑else に切り替える
このガイドラインに沿って実装すれば、Java の三項演算子を 安全かつ効果的 に活用でき、コードベース全体の品質向上につながります。