Java 8 Optional 能否解決 NPE ?

答案

  Java 8 的 Optional 並不能解決 NPE,但是可以預防 NPE 的發生!

空指針問題

  作爲 Java 開發工程師,如果說 NullPointerException 是最常見的異常且沒有之一,肯定是沒有人反駁的.相對於其他異常, NPE 可謂是司空見慣了.
  NPE 的出現可以簡單歸納爲兩種情況

1. 數據問題
2. 代碼問題

  作爲研發人員,這兩種情況是如何導致NPE,就不在此贅述.我們來仔細論述一下這兩種情況下導致的NPE 該如何處理!

數據問題

  首先是數據問題,這種情況的下發生的 NPE,我們應該首先處理數據,然後排查髒數據產生的原因,而不是在代碼中寫下Objects.isNull(xxx).
  這種情況下的 Optional 完全沒有發揮餘地,Optional 是不可能無中生有的.也解決不了業務流程中引入髒數據的問題,並且如果強行使用 Optional 令代碼暫時不報錯,不拋異常,非常可能導致使用數據的後序流程產生更大的錯誤和代價!

代碼問題

  作爲代碼問題導致的NPE,我們是否可以使用 Optional 解決呢?就如開篇答案所寫是不可能的!
在網絡上絕大多數的文章中都在寫 Optional 是如何優雅的解決NPE,並列上了一些看似正確的代碼.比如這樣的代碼



  但是我們仔細閱讀代碼,並分析解決方案.我們可以看到這些所謂的解決方案無非是使用了 Optional 實現了更優雅的寫法,是真的解決了 NPE 麼?並沒有.
  現實中的業務代碼中 NPE 的出現,絕大多數情況是我們業務經驗缺失或思考不夠嚴謹,忽略了邊界條件和忘記判空所引發的.這時候使用 Optional 也只是一種更優雅的補償方式,而並不是一種所謂的徹底解決方案!

Optional 存在的意義

  我們在上文中對現有大多數人的想法和代碼做了一番批判,那是不是 Optional 的存在就是無意義的呢?
  那當然不是, Optional 真正的作用是警示並預防NPE 的出現,並將隱形的業務知識顯性化!我們慢慢分析這句話.

警示並預防NPE

  官方的文檔中建議表示不要將 Optional 作爲方法入參和對象屬性,並建議返回值使用Optional.
  我們看以下代碼來理解官方的這個建議

    /**
     * 根據輸入數據獲取一個隨機數
     *
     * @param upperLimit 上限
     * @param lowerLimit 下限
     * @return ranomNumber
     */
    public Optional<Integer> getRandomNumber(Optional<Integer> upperLimit, Optional<Integer> lowerLimit) {

        int max = Objects.isNull(upperLimit) ? Integer.MAX_VALUE : upperLimit.orElse(Integer.MAX_VALUE);
        int min = Objects.isNull(lowerLimit) ? Integer.MIN_VALUE : lowerLimit.orElse(Integer.MIN_VALUE);

        Random r = new Random();
        OptionalInt first = r.ints(min, max).findFirst();

        if (first.isPresent()) {
            return Optional.of(first.getAsInt());
        }
        return Optional.empty();
    }

    /**
     * 不使用 Optional
     */
    public Integer getRandomNumber(Integer upperLimit, Integer lowerLimit) {

        int max = upperLimit != null ? upperLimit : Integer.MAX_VALUE;
        int min = lowerLimit != null ? lowerLimit : Integer.MIN_VALUE;

        Random r = new Random();
        OptionalInt first = r.ints(min, max).findFirst();

        if (first.isPresent()) {
            return first.getAsInt();
        }
        return null;
    }

  觀察上面這個簡單的案例,我們並沒有因爲使用了 Optional 就少做了判空操作,反而因爲 Optional的使用,需要多一次對Optional 對象的判空
  只要我們邏輯嚴謹完全可以不用Optional,就寫出健壯的代碼,反而因爲 Optional 做爲入參的傳入,我們需要額外的代碼對 Optional 判空處理.

public static void main(String[] args) {

        // 響應類型暗示了出參爲空的可能
        Optional<Integer> randomNumber = getRandomNumber(Optional.empty(), Optional.of(1));
        Integer integer = randomNumber.orElse(0);
        System.err.println("獲取到的隨機數" + integer.toString());

        // 不閱讀源碼,對出參爲空的情況並不知情
        Integer random = getRandomNumber(null, 1);
        // 可能出現NPE
        System.err.println("獲取到的隨機數" + random.toString());
    }

  但是方法返回值使用 Optional<Integer> 卻實實在在的提醒了調用 getRandomNumber 方法的研發人員,獲取到的隨機數可能是空的,要隨時思考空值對系統產生惡劣影響和破壞的可能.
  至此,我們已經可以體會官方推薦的含義了.將 Optional 做爲方法返回值是有實際好處,並有意思的,能夠警示我們,並督促我們提前處理可能爲空的情況!

業務知識顯性化

  作爲研發人員,尤其剛入職新公司或剛剛接觸新業務的研發,寫出 NPE 代碼的可能性是最高的,這是因爲他們的業務知識的積累不夠和缺失,對一些常用業務方法出現空值的情況並不知情,也並未深入源碼閱讀的結果.
  使用 Optional 可以提醒他們,並明確業務返回值爲空的可能性,並謹慎處理返回值.將部分隱藏在大腦和源碼中的隱形業務知識,通過代碼實現了顯性的表達!降低了出現低級錯誤的可能,和不知情帶來的過錯.

End

  Java 8 Optional 能否解決 NPE ? 不能!
  Java 8 Optional 有實踐和使用的意義麼 ? 非常有!
  至此,標題中的問題,我們已經論述清楚,如有不同意見或想法,可以下方評論,共同進步!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章