前言
相信各位 Javaer 對於三目運算符(三元運算符)都不陌生,較多情況下使用三目運算符即可節省一個 if-else 語句賦值的編寫,筆者也是經常使用三目運算符,前段時間遇到了一點三目運算符的類型轉換的坑,便在此記錄一下。望更多的朋友能夠避免,也加深我對三目運算符的理解。
三目運算符使用
閒話不說,直接上代碼。
/**
* 三目運算符使用
*/
public void ternaryOperator() {
boolean switchLog = true;
// 基本類型
int intValue = switchLog ? 1 : 0;
// 引用類型
String strValue = switchLog ? "true" : "false";
}
三目運算符可表達爲:布爾表達式 ? 表達式1 : 表達式2
布爾表達式:其結果決定三目運算符的分支情況
表達式1:當布爾表達式爲 true,則執行
表達式2:當布爾表達式爲 false,則執行
類型轉換
在三目運算符的使用過程中,即支持基本類型,也支持引用類型。值得注意的一點是,引用類型爲 null 時,也是支持給引用類型賦值的。
特別的,我們關注到了基本類型和其封裝類型,於是有了下述代碼。
@Test
public void intInteger() {
boolean switchLog = true;
Integer oneExpre = null
Integer result = switchLog ? oneExpre : 0;
System.out.println(result);
}
猜想下這個程序的 result 返回什麼,是 null 麼?爲什麼?
試着運行下測試,發現得到以下報錯:
java.lang.NullPointerException
at club.chenlinghong.demo.ternaryoperator.TernaryOperatorDemo.intInteger(TernaryOperatorDemo.java:28)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70
NPE,有意思,命名沒有任何問題呀,就算是 switchLog 爲 true 嘛,也就把 null 值賦給 result 嘛,這不科學。
我們再來嘗試下另外一種寫法:
@Test
public void intInteger() {
boolean switchLog = true;
Integer oneExpre = null;
Integer result = switchLog ? oneExpre : new Integer(0);
System.out.println(result);
}
再次執行,居然沒報錯。result 爲 null。
嘗試着分析一下
報錯的情況:
這種情況下,“表達式1” 結果是 null,而“ 表達式2 ” 是基本類型。大膽猜想一下,當執行時,先執行 “表達式1” 的得到返回結果 null ,此時 null 是一個特殊的值,並不知道其“具體的類型”,然後嘗試着和 “表達式2” 進行統一類型,發現 “表達式2” 是 int 類型,並進行強轉或者封裝類型轉基本類型,報NPE。
未報錯的情況:
和報錯情況不同的是,“表達式2”本身就是 Integer 類型,封裝類型本身支持 null 類型賦值,便省去了類型強轉的步驟,則不報錯。
結論與思考
這是一個有趣的實驗,同時也是一個很坑的點。對於這些細枝末節的知識點(坑點),可能在平時學習和編碼中都很少遇到,但是在遇到的時候,可能需要查很久很久的代碼才能找到。
注重知識點的累計,多一次在學習中踩坑,就少一次在生產上的事故,哈哈哈哈哈 ~ _ ~
源碼
Github鏈接,歡迎star,follow。