單純的使用Optional並不能很好的解決業務中的NPE問題 - 簡書
https://www.jianshu.com/p/f21afe47a3e7
單純的使用Optional並不能很好的解決業務中的NPE問題
工作中經常會遇到NPE(NullPointException)問題怎麼處理,得到最多的回答就是Optional來處理,但是單純的使用Optional並不能很好的解決業務中的NP問題。所以整理出日常工作中處理NPE問題的思路,供參考。
先看一下思路:
解決NPE的思路
01 錯誤的姿勢
01.01 避免入參使用 Optional
原因:
1. 干擾閱讀者, 讓方法的意圖模糊
2. 別人在調用該方法時, 還需要專門new一個Optional後才能傳進來, 顯得調用繁瑣
日常工作中有是看到如下代碼。
public void execute(Optional<String> nameOptional) {
...
}
如果方法的某個參數可能爲null,那麼可以使用方法重載,而不是傳入一個null。因爲代碼的實現上揭示業務意圖要要好於只表示業務意圖,因爲在業務上null沒有業務意義,如果出現null,而又需要處理相關邏輯,那麼可以使用方法重載。
另外,方法調用者將很清晰起作用的參數,也不需要爲了調用某個方法而構造Optional。
public void execute() {
execute(null);
}
public void execute(String name) {
...
}
上面的代碼避免了對外方法的意圖模糊,同時也避免了部分的重複代碼。
01.02 避免多行 if() 語句
原因:
1. 如果在使用Optional, 還是出現多行的 if(xxxOptional.isPresent()) ...之類代碼, 那跟不用Optional的 if(xxx == null) 沒區別, 節約不了什麼成本
“使用了Optional,也沒有感覺邏輯變變簡單啊,每次都得get()方法”,這是工作中聽到的剛剛接觸Optional的開發人員的表述。
問題在哪裏呢?如果我們有一把把核桃夾子,但是每次用這把核桃夾子做的事情都是砸核桃,那麼就發現和用錘子砸也沒有什麼區別,結果都是碎了一地。
我們都說代碼時重構出來的,而不是設計出來的。所以實現的時候如果對Optional不熟悉可以使用if來判斷,重構的時候可以試着尋找一些能夠有更好的解決辦法。因爲switch語句本來就是壞味道,尋找的方向可以先看看Optional除了提供了get方法,還提供了哪些方法,它們都是做什麼用途的。
02 正確使用姿勢
02.01 返回值中用Optional顯式處理
如果實際實現中需要我們將null值返回,我們可以return一個Optioanl對象回去,這樣該方法的調用者就必須就知道返回中有可能爲null,並對其進行特定的處理。
public Optional<Data> execute() {
...
return Optional.empty();
}
02.02 適當的時候使用Exception
並不是找不到結果我們就只能選擇返回Optional.empty(),除非邏輯需要繼續執行而不期望中斷,否則我們可以直接選擇拋出異常。
例如,根據username、password尋找用戶的方法,如果找不到用戶,可以直接拋出InvalidUsernameOrInvalidPasswordException。
public User login(String name, String password) {
...
throw new InvalidUsernameOrInvalidPasswordException();
}
那麼,外層要麼進行異常處理,要麼Project進行全局異常捕獲,統一進行處理。
02.03 使用Optional Lambda
在01.02中提到了因爲對Optional提供的方法不熟悉,而感覺和null值判斷沒有多大區別。那麼推薦根據業務場景來選擇Optioanl Lambda的寫法。
String name = computer.flatMap(Computer::getSoundcard)
.flatMap(Soundcard::getUSB)
.map(USB::getVersion)
.orElse("UNKNOWN");
上面寫法要比下面的寫法清晰很多。當然這需要保持好奇心,多學習一點工具類提供的方法,同時別總是製作簡單的搬磚工作。
String version = "UNKNOWN";
if (computer != null) {
Soundcard soundcard = computer.getSoundcard();
if (soundcard != null) {
USB usb = soundcard.getUSB();
if (usb != null) {
version = usb.getVersion();
}
}
}
參考