條件運算子 booleanExp ? exp1 : exp2 看似簡單,其結果型別推導規則卻藏著許多 Java 開發者易踩的坑。本文依據 JLS(Java Language Specification)條文,配合實測程式碼,系統拆解三大規則與邊界情況。
exp1、exp2 型別相同 → 結果為該型別。byte/short/char(稱 T),另一邊為 int 常數運算式 且值在 T 範圍內 → 結果為 T。byte/short/char → int → long → float → double)提升至較寬型別。char x = 'A'; // 65
int i = 0;
System.out.println(true ? x : 0); // 輸出 'A' (char)
System.out.println(true ? x : 1111111110); // 輸出 65 (int)
System.out.println(false ? i : x); // 輸出 65 (int)
| 運算式 | exp1 型別 | exp2 型別 | 規則匹配 | 結果型別 | 輸出 |
|---|---|---|---|---|---|
true ? x : 0 | char | int 常數 (0) | 規則 2:0 在 char 範圍內 | char | 'A' |
true ? x : 1111111110 | char | int 常數 (超出 char 範圍) | 規則 3:char → int 提升 | int | 65 |
false ? i : x | int 變數 | char 變數 | 規則 3:無常數,雙向提升 | int | 65 |
關鍵差異:0 是編譯期常數,i 是變數。常數允許窄化,變數不允許。
Test t = false ? new Object() : new Test(); // 編譯錯誤
exp1:Object,exp2:Test(假設 Test extends Object)。Object。Object 無法指派給 Test 變數 → 編譯錯誤。修正:強制轉型 (Test) (false ? new Object() : new Test()) 或調整邏輯避免此結構。
true ? 1 : Integer.valueOf(2) → 自動拆箱提升為 int,但 null 會拋 NPE。true ? 1 : 1L → 結果 long,賦給 int 需強制轉型。true ? "A" : 1 → 非字串端 toString() 後結果 String(特殊規則)。condition ? methodA() : methodB() 只執行一邊,若依賴副作用需改用 if-else。條件運算子型別推導遵循「常數優先窄化、否則雙向提升」原則。理解 編譯期常數 與 變數 的本質差異,是避免「明明型別相容卻編譯失敗」或「運行時意外拆箱 NPE」的關鍵。寫法複雜時,寧可拆成 if-else 明確標註型別,亦可加強可讀性。