ITEM 71: AVOID UNNECESSARY USE OF CHECKED EXCEPTIONS
??許多Java程序員不喜歡檢查異常,但如果使用得當,它們可以改進 API 和程序流部。與返回代碼和未檢查的異常不同,它們迫使程序員處理問題,提高了可靠性舀透。與此同時,在 API 中過度使用受控異常會使它們使用起來非常不愉快决左。如果一個方法拋出檢查過的異常愕够,調用它的代碼必須在一個或多個 catch 塊中處理這些異常走贪,或者聲明它拋出這些異常并讓它們向外傳播。無論哪種方式惑芭,都會給 API 的用戶帶來負擔坠狡。Java 8 中的負擔增加了,因為拋出已檢查異常的方法不能直接在流中使用(item 45-48)遂跟。
??如果異常情況不能被 API 的正確使用所阻止逃沿,并且使用 API 的程序員在遇到異常時可以采取一些有用的操作,那么這種負擔可能是合理的幻锁。除非這兩個條件都滿足凯亮,否則不檢查異常是合適的。作為一個測試哄尔,問問你自己將如何處理異常触幼。這是我們能做到的最好的結果嗎?
} catch (TheCheckedException e) {
throw new AssertionError(); // Can't happen!
}
??或者:
} catch (TheCheckedException e) {
e.printStackTrace(); // Oh well, we lose.
System.exit(1);
}
??如果程序員不能做得更好,使用未檢查的異常也許更好究飞。
??如果檢查異常是方法拋出的唯一檢查異常置谦,則由檢查異常給程序員造成的額外負擔將大大增加。如果還有其他異常亿傅,則該方法必須已經出現在 try 塊中媒峡,而這個異常最多需要另一個 catch 塊。如果方法拋出單個檢查異常葵擎,此異常是該方法必須出現在 try 塊中而不能直接在流中使用的唯一原因谅阿。在這種情況下,最好問問自己是否有辦法避免被檢查的異常酬滤。
??消除檢查異常的最簡單方法是返回所需結果類型的 Optional (item 55)签餐。該方法不拋出檢查異常,而只是返回一個空的結果盯串。這種技術的缺點是氯檐,該方法不能返回任何說明其無法執(zhí)行所需計算的額外信息。相反体捏,異常具有描述性類型冠摄,并且可以導出方法來提供額外的信息(item 70)。
??通過將拋出異常的方法分解為兩個方法几缭,還可以將檢查異常轉換為未檢查異常河泳,第一個方法返回一個布爾值,指示是否將拋出異常年栓。這個API重構將調用序列由
try {
obj.action(args);
} catch (TheCheckedException e) {
... // Handle exceptional condition
}
??轉換為:
// Invocation with state-testing method and unchecked exception
if (obj.actionPermitted(args)) {
obj.action(args);
} else {
... // Handle exceptional condition
}
??這種重構并不總是合適的拆挥,但在適當的地方,它可以使 API 使用起來更愉快某抓。雖然后一種調用序列并不比前一種更漂亮纸兔,但重構后的 API 更靈活惰瓜。如果程序員知道調用將成功,或者在調用失敗時滿足于讓線程終止食拜,重構也允許這個微不足道的調用序列:
obj.action(args);
??如果您懷疑普通的調用序列將是規(guī)范鸵熟,那么API重構可能是合適的。生成的API是聲明測試方法 API (item 69)應用相同的警告:如果一個對象是被并發(fā)地訪問沒有外部同步或受外部誘導狀態(tài)轉換负甸,這種重構是不恰當的流强,因為對象的狀態(tài)可能會在調用 actionPermitted 和 action 之間改變。如果一個單獨的 actionPermitted 會重復 action方法的工作呻待,那么基于性能原因打月,重構可能會被排除。
??總之蚕捉,當有節(jié)制地使用時奏篙,檢查異常可以提高程序的可靠性迫淹;當過度使用時秘通,它們會使 API 使用起來很痛苦。如果調用方無法從失敗中恢復敛熬,則拋出未檢查的異常肺稀。如果可能進行恢復,并且希望強制調用者處理異常條件应民,那么首先考慮返回一個Optional话原。只有在失敗的情況下,且需要提供足夠的信息時诲锹,才應該拋出檢查過的異常繁仁。