2015-09-08 42 views
1

在Java中,對象默認情況下初始化爲null不使用空值編寫代碼

我最近碰到這個答案出來,在做研究,妥善處理NPE:https://softwareengineering.stackexchange.com/a/187225

以下是從後我的主要外賣:

如果你從來沒有設置一個變量爲null你永遠不會有意外的空。

不允許空參數

我一直想知道如何用Java和Android處理這個問題。我在開發人員儀表板上看到的許多崩潰報告都是NPE,而且我注意到在我的代碼中,我使用null檢查以查看是否可以繼續操作。

我梳理通過我的代碼庫試圖刪除null和默認實例儘可能的,但我運行到這個地方出現不可能幾個領域,我不知道如何解決它的錯誤來自這些變量。

例如,使用Google Maps API時,必須聲明GoogleMap對象。我目前設置的方式是將變量聲明爲我的Activity的成員,然後在onCreate()期間初始化它。不幸的是,在onResume()期間,我撥打.clear()GoogleMap,偶爾該字段將是null。我無法在自己的環境中重現這個錯誤,這導致我在處理NPE時做了更多的閱讀。

在另一個(非常受歡迎)的問題在這裏:Avoiding != null statements,頂部和接受的答案建議三個策略來解決這個問題。

  1. assert聲明。從表面上看,這看起來更像是單元測試/調試代碼,特別是因爲Java在默認情況下忽略了assert
  2. 空對象模式。這可能是提出的解決方案中最具吸引力的解決方案,因爲它最終看起來更加清爽。但是,在使用外部庫時,我不確定這是否是一種選擇。擴展一個外部庫看起來很麻煩。
  3. try/catch。這很困難。由於null在Java中非常普遍,所以代碼最終會緊密結合try/catch來處理可能爲null的所有內容,或者最終導致大量代碼被阻止,而捕獲錯誤將無法保證處理善後事宜的精度。

我的問題是,有沒有另一種選擇?也許我可以使用的編碼範例或某種內置的解決方案可以解決這個問題?

理想情況下,我想要一個解決方案,我不會崩潰,失去數據可靠性或失去應用程序功能。

注意 - 這不是關於我的代碼問題的問題,這是一個關於如何防守編寫代碼,以防止意外NPE在默認初始化null一個語言問題。

+0

此外,請檢查Java 8中引入的(最終)[可選的](構造)「構造」 –

+0

爲什麼要清除地圖會使它爲空? @ɐuıɥɔɐɯ不是Android的專家,但不知道您可以在Android SDK中使用此工具 – Dici

+0

清除地圖不會使其爲空。出於某種原因,偶爾地,當達到'.clear()'語句時,map *已經是null *。 – nukeforum

回答

0

空,本身並不是可以避免的。任何尚未分配併成功編譯的聲明引用在Java中都被視爲null,原因很多:堆中有垃圾值。 Java會跟蹤您的分配,並在內存位置返回null,這些內存位置仍然包含應用程序分配到堆塊時的垃圾值。它可以防止你獲得像整數這樣的東西的價值,認爲它們屬於你,當它們在應用程序啓動之前碰巧使用相同地址的其他應用程序遺留下來的時候。

列出的每個策略都有自己的用途,沒有一個是處理可能的NPE的全部方法。

斷言通常是要避免的東西。如果條件不符合,它會導致暫停聲明。換句話說,這不是一個優雅的退出。 A!= null語句實現與斷言相同的目標,但可以選擇對條件作出響應而不是停止程序。

的try/catch是在強大的情況下,你可以運行到比賽的條件。在調用大量使用網絡相關操作的異步庫和庫時,這非常有用。 GoogleMaps有很多方法可以根據新的位置或視圖框架獲取更新的信息,並且在某些情況下,您可能會試圖對由圖書館管理的對象進行調用,該對象可能是中間刷新的。當我無法輕易複製NPE時,我通常會假設競爭狀態。

最後,我看不出在試圖避免!= NULL檢查任意值。它捕捉並允許你迴應try/catch不適合的所有條件,並且不需要花費任何費用。所有布爾操作都是O(1)操作。爲了看到性能下降甚至1秒,您必須重複使用十億甚至更多。不值得頭痛消除。

+0

你的第一段有瑕疵。字段被*定義爲空引用,這不是一個考慮因素。變量*無法查詢,直到[明確分配](https://docs.oracle.com/javase/specs/jls/se7/html/jls-16。html),正如編譯器強制執行的那樣,所以在此之前不需要考慮值。 – Andreas

+0

如果遇到競態條件,您沒有正確編寫多線程邏輯。嘗試抓住不是這個答案。 – Andreas

+0

安德烈亞斯對這個特定的答案有一些非常好的觀點,但我覺得整體主題是正確的,並且與安德烈亞斯的答案有些平行。 @Stephan,你能評論Andreas的評論嗎?或者作爲對答案的編輯?我想接受這個答案,但我不想通過接受有潛在缺陷的答案來誤導未來的讀者。 – nukeforum

1

我真的不明白爲什麼空檢查被認爲是不好的,因爲爲了防止頂級異常使用@NonNull@Nullable註釋,靜態分析真的有幫助。

+0

這對編譯時'null'問題有很大的幫助,但不幸的是,它不能幫助防止運行時問題,就像我在我的例子中描述的那樣。 – nukeforum

+0

來自android sdk的大部分類都有標記爲可爲空的方法,反之亦然,應該不是什麼大問題。但是如果你使用別人的代碼,除了在運行時檢測到它之外,沒有簡單的解決方案。我建議你不要檢查一切爲null,因爲這有點兒開銷。 – dtx12

0

我發現引用文章的前提的例外。它指出:

的bug 不是NullPointerException被拋出,的bug早在你的代碼,其中一個變量設置爲null沒有你希望它是。

我想說的bug是不期待值是null,所以錯誤是什麼地方的NPE發生。

就像任何其他工具一樣,它的使用方式也是最重要的。使用null值來表示存在沒有值是完全正確的,只要它是明確的。

這就是說,確實null被過度使用,並且有時被替換爲一個例外。但這一切都取決於環境。

看看Java中的BlockingQueue接口。要從隊列中刪除一個值,您可以調用remove(),如果隊列爲空,則會拋出異常,或者您可以撥打poll()返回null。如果您期待值,請使用remove()。如果您知道排隊可能爲空,並且您將處理該問題,請使用poll()。不要使用remove()與try-catch進行流量控制。

+0

有趣的解釋。你不考慮避免'null'狀態完全解決不期望值爲'null'的錯誤嗎? – nukeforum

+0

@nukeforum如果返回值可以是「特殊的」,無論是「null」還是「empty」,您可能仍然需要處理它。一個特殊的價值應該永遠記錄下來,而且調用者可能總是需要以不同的方式處理它。一個空對象可以防止NPE,但它不能保證你的代碼正確處理特殊值。事實上,如果沒有錯誤,它可能會出現相反的效果。使用Java 8的'Optional'或類似的pre-8構造,明確區分「正常」和「特殊」,並且調用者*必須*相應地採取行動。 – Andreas

+0

>如果返回值可以是「特殊」的,無論是「空」還是「空」,您可能仍然需要處理它。 這正是我一直在碰到的。我試圖避免在任何地方檢查'null',但除非我檢查,否則我所能做的就是吞下NPE或通過空或空對象丟失數據。 'BlockingQueue'看起來很有趣,但我想我可能會回頭看看'!= null'。 – nukeforum

0

我對自己提出了同樣的問題,並最終拋出異常並將其緩存在代碼的頂層。這樣做,我不得不宣佈try try一次,無條件。當然,它需要保證,如果某些值預計不會有或者是無效的,拋出該異常:

bool isEquals(string name) 
    { 
     if(tempData == name.toUpperCase()) // if name is null,a null reference will be throw 

     //... 
    } 

即使你無法擺脫的NPE和try/catch,你可以對應用程序失敗的地方有信心,這是一個很大的優勢。