參考
- 伯樂(lè)在線(xiàn) - 你真的會(huì)用 Java 中的三目運(yùn)算符嗎挤渔?
- 三目運(yùn)算符的官方文檔
- Java_Chuck - JAVA筆記-三目運(yùn)算符返回值規(guī)則
問(wèn)題
下面這段代碼竟然報(bào)了NullPointerException
異常.
public static void main(String[] args) { Map<String, Boolean> map = new HashMap<>(); // Exception in thread "main" java.lang.NullPointerException boolean b = (map!=null ? map.get("test") : false); }
問(wèn)題來(lái)源是Java的Autoboxing和Boxing機(jī)制.
三目運(yùn)算符求值順序
首先是右結(jié)合的.
The conditional operator is syntactically right-associative (it groups right-to-left). Thus, a?b:c?d:e?f:g means the same as a?b:(c?d:(e?f:g)).
其次再關(guān)注整體的返回值類(lèi)型是什么, 首先 ? Expression1 : Expression2
中的兩個(gè)表達(dá)式, 要么是同一個(gè)類(lèi)型, 要么是能夠相互轉(zhuǎn)換的, 但是最后會(huì)統(tǒng)一為一個(gè)類(lèi)型, 規(guī)則(下面只摘錄了一部分, 完整的可以去三目運(yùn)算符的官方文檔查看)如下:
If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression.
If one of the second and third operands is of primitive type T, and the type of the other is the result of applying boxing conversion (§5.1.7) to T, then the type of the conditional expression is T.
If one of the second and third operands is of the null type and the type of the other is a reference type, then the type of the conditional expression is that reference type.
對(duì)于上面的boolean b = (map!=null ? map.get("test") : false);
:
- map.get("test") 的返回值是Boolean
- false 是原始類(lèi)型boolean
根據(jù)規(guī)則2
, boolean
是原始類(lèi)型, 而Boolean
類(lèi)型是boolean
裝箱后的結(jié)果, 即Boolean
. 所以最終的返回值類(lèi)型為boolean
.
然后開(kāi)始對(duì)整體求值, 首先map != null
肯定是成立的, 所以會(huì)對(duì)map.get("test")
求值, 因?yàn)閙ap中不存在test
這個(gè)key, 所以get方法返回了null
, 這也是整體的返回值. 因?yàn)?code>map.get()返回值類(lèi)型是Boolean
, 而因?yàn)橐?guī)則2
, 所以會(huì)將map.get()
對(duì)的返回值進(jìn)行拆箱操作, 即調(diào)用Boolean.booleanValue()
方法, 而此時(shí)map.get()
的返回值是一個(gè)null
, 所以出現(xiàn)了NullPointerException
.
經(jīng)過(guò)進(jìn)一步測(cè)試, 有以下結(jié)果:
根據(jù)B
那句賦值語(yǔ)句來(lái)看, 應(yīng)該是編譯器判斷又要從boolean
轉(zhuǎn)換回Boolean
所以map.get()
的結(jié)果應(yīng)該不會(huì)再拆箱了.
Map<String, Boolean> map = new HashMap<>();
// Exception in thread "main" java.lang.NullPointerException
// boolean b = (map!=null ? map.get("test") : false);
// 沒(méi)有異常
// Boolean B = (map!=null ? map.get("test") : false);
// Exception in thread "main" java.lang.NullPointerException
// if ((map!=null ? map.get("test") : false)){
// ;
// }
// Exception in thread "main" java.lang.NullPointerException
// boolean b1 = (map!=null ? map.get("test") : Boolean.FALSE);