前言
看到好多文章都是推薦采用Optinal的,而經常我遇到問題的時候就想:如果設計成optional的話就不會忽略這種NullPointException錯誤了。然而,optional並不是想用就隨便用的。今天花了10分鍾追蹤一個bug,根源就是optional濫用。
問題描述
API返回失敗,沒有描述原因。看着蛋疼,因為公開的API不方便返回錯誤詳情。於是查log,發現錯誤日志的message為:No value present
。沒搞清楚這個錯誤信息是哪一層跑出來的。需要進一步跟蹤。A=>B=>C=>D
,一直追蹤到C層才找到問題。
問題代碼如下:
public FieldBuilder withSubcategoryId(Optional<String> id) {
this.id = id.get();
return this;
}
這是一個創建工廠類,負責創建一個可以使用對象。所有的字段都采用了Opetional的包裹。這個是對象,理應不包含業務邏輯,應該沒有錯誤異常。如果有異常應該顯式的throws
出來,不然這個非檢查性異常將在出現bug的時候難以定位。而這里確實有一個異常沒有捕獲,而且也不能保證不會發生,甚至就是這里引起的bug:java.util.Optional#get
源碼如下:
/**
* If a value is present in this {@code Optional}, returns the value,
* otherwise throws {@code NoSuchElementException}.
*
* @return the non-null value held by this {@code Optional}
* @throws NoSuchElementException if there is no value present
*
* @see Optional#isPresent()
*/
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
雖然沒有顯式的拋出異常,但在javadoc中寫清楚了會出現的問題。而我們這些新手則沒有認真看文檔就想當然的采用了。以為當內容為null
的時候get出來的還是null
。
Find Uage找這個Builder的用法發現:
new FieldBuilder().withSubcategoryId(Optional.ofNullable(entity.getSubcategoryId()))
這里直接使用了Optional.OfNullable
。然而,我們知道在下一步中會調用get,get的時候回判斷是否是null
,null
會拋出異常。這簡直就是自己挖坑,寫一個條件拋異常,而傳參數又專門去符合這個條件。前面也沒有校驗,外面也沒有捕獲異常,最終導致異常直接一路拋出到API外層去了。
結論
Optional不要濫用,Optional不是安全的隨便用的,Optional用的時候記得捕獲異常。