多年以來,我一直沒法得到如下問題的正確答案:爲何某些開發人員如此反對檢查異常? 我進行了無數次對話,閱讀了博客上的東西,閱讀了布魯斯·埃克爾(Bruce Eckel)所說的話(我看到的第一個反對他們的人)。 程序員
我目前正在編寫一些新代碼,並不是常注意我如何處理異常。 我正在嘗試查看「咱們不喜歡檢查的異常」人羣的觀點,但我仍然看不到它。 數據庫
個人每一次對話都以相同的問題爲結尾而結束...讓我進行設置: 編程
整體而言(根據Java的設計方式), 數組
Error
是針對不該該被抓到的東西(VM有花生過敏症,有人在上面撒了一罐花生) RuntimeException
適用於程序員作錯的事情(程序員走出了數組的結尾) Exception
( RuntimeException
除外)適用於程序員沒法控制的事情(寫入文件系統時磁盤已滿,已達到該進程的文件句柄限制,而且您沒法打開其餘文件) Throwable
只是全部異常類型的父級。 我聽到的一個常見論點是,若是發生異常,那麼開發人員要作的就是退出程序。 架構
我聽到的另外一個常見論點是,檢查異常會使重構代碼更加困難。 框架
對於「我要作的就是退出」參數,我說即便您要退出,也須要顯示一條合理的錯誤消息。 若是您只是在處理錯誤,那麼當程序退出時若是沒有明確說明緣由的話,您的用戶將不會過度高興。 函數
對於「它很難重構」人羣,這代表未選擇適當的抽象級別。 與其聲明一個方法拋出IOException
而不是將IOException
轉換爲更適合發生的異常。 測試
對於將Main與catch(Exception)
(或在某些狀況下, catch(Throwable)
進行包裝以確保程序能夠正常退出,我沒有任何問題-但我老是捕獲所需的特定異常。至少顯示適當的錯誤消息。 spa
人們從不回覆的問題是: 線程
若是拋出
RuntimeException
子類而不是Exception
子類,那麼您如何知道應該捕獲的內容?
若是答案是catch Exception
那麼您也將以與系統異常相同的方式處理程序員錯誤。 對我來講這彷佛是錯誤的。
若是捕獲Throwable
則將以相同的方式處理系統異常和VM錯誤(等等)。 對我來講這彷佛是錯誤的。
若是答案是僅捕獲已知的異常,那麼您如何知道拋出了哪些異常? 當程序員X拋出一個新異常而忘記捕捉它時,會發生什麼? 對我來講這很危險。
我會說顯示堆棧跟蹤的程序是錯誤的。 不喜歡檢查異常的人會不會有這種感受?
所以,若是您不喜歡檢查異常,是否能夠解釋爲何不這樣作並回答未獲得回答的問題?
編輯:我不是在尋找什麼時候使用任何一種模型的建議,我正在尋找的是爲何人們從RuntimeException
擴展,由於他們不喜歡從Exception
擴展和/或爲何他們捕獲異常而後從新拋出RuntimeException
而不是向其方法添加拋出。 我想了解不喜歡檢查異常的動機。
嘗試僅解決未解決的問題:
若是拋出RuntimeException子類而不是Exception子類,那麼您如何知道應該捕獲的內容?
該問題包含似是而非的推理恕我直言。 僅僅由於API告訴您它拋出了什麼並不意味着您在全部狀況下都以相同的方式處理它。 換句話說,您須要捕獲的異常取決於使用組件引起異常的上下文。
例如:
若是我正在爲數據庫編寫鏈接測試器,或者要檢查用戶輸入的XPath的有效性的東西,那麼我可能想捕獲並報告該操做引起的全部已檢查和未檢查的異常。
可是,若是我正在編寫處理引擎,則可能會以與NPE相同的方式對待XPathException(已檢查):我會讓它運行到工做線程的頂部,跳過該批處理的其他部分,登陸問題(或將其發送給支持部門進行診斷),並留下反饋讓用戶與支持人員聯繫。
咱們已經看到了一些有關C#首席架構師的參考。
這是Java專家關於什麼時候使用檢查的異常的另外一種觀點。 他認可其餘人提到的許多負面因素: 有效例外
好的...受檢查的異常不是理想的狀況,但有一些警告,但確實能夠達到目的。 建立API時,某些特定狀況下的失敗是該API的約定。 當在諸如Java之類的高度靜態類型化語言的上下文中,若是一我的不使用檢查的異常,則必須依靠即席文檔和約定來傳達錯誤的可能性。 這樣作會消除編譯器帶來的處理錯誤的全部好處,而您徹底會被程序員的善意所取代。
所以,一個刪除Checked異常,例如在C#中進行的操做,那麼一我的如何以編程和結構方式傳達錯誤的可能性? 如何通知客戶代碼這種錯誤可能發生而且必須加以解決?
在處理受檢查的異常時,我聽到了各類各樣的恐懼,這是能夠確定的,但未經檢查的異常也是如此。 我說等幾年,當API堆疊到很深的層次時,您會乞求某種結構化方法的回報來傳達故障。
以這種狀況爲例,該異常被拋出到API層底部的某個地方,而且因爲沒有人知道甚至可能發生此錯誤而冒泡了,即便這種錯誤類型在調用代碼時很是合理扔掉它(例如FileNotFoundException而不是VogonsTrashingEarthExcept ...在這種狀況下,咱們是否處理它都沒有關係,由於沒有東西能夠處理它了)。
許多人爭辯說,沒法加載文件幾乎老是該過程的末日,它一定會致使可怕而痛苦的死亡。 因此是的..肯定...肯定..您爲某些內容構建了一個API,並在某個時刻加載了文件...做爲該API的用戶,我只能作出響應...「您到底該決定個人時間程序應該崩潰!」 固然能夠,由於能夠選擇在其中吞噬異常而且不留任何痕跡,或者具備比Marianna溝槽更深的堆棧跟蹤的EletroFlabbingChunkFluxManifoldChuggingException,我會絕不猶豫地選擇後者,但這是否意味着這是處理異常的理想方法? 咱們能夠不在中間的地方,在異常遍歷到新的抽象級別時會重鑄幷包裝該異常,以便它實際上意味着什麼嗎?
最後,我看到的大多數論據是「我不想處理異常,許多人不想處理異常。檢查異常迫使我去處理它們,所以我討厭檢查異常」。將其釋放到goto的鴻溝地獄只是愚蠢的,缺少判斷力和遠見。
若是咱們消除了檢查異常,咱們也能夠消除函數的返回類型,而且老是返回「 anytype」變量……那會使生活變得如此簡單,不是嗎?
不須要檢查異常的充分證據是:
若是Java在使用I / O之類的核心庫時能爲我提供選擇使用什麼的選項 ,我感到很高興。 Like提供了兩個相同類的副本-一個用RuntimeEception包裝。 而後咱們能夠比較人們會用什麼 。 可是,到目前爲止,許多人最好仍是在Java或其餘語言之上尋求一些框架。 像Scala同樣,JRuby也能夠。 許多人只是認爲SUN是正確的。
在談論異常時,我老是參考Eric Lippert的Vexing異常博客文章。 他將異常納入如下類別:
OutOfMemoryError
或ThreadAbortException
。 ArrayIndexOutOfBoundsException
, NullPointerException
或任何IllegalArgumentException
。 Integer.parseInt
引起NumberFormatException
而不是提供Integer.tryParseInt
方法,該方法在解析失敗時返回布爾值false。 FileNotFoundException
。 API用戶:
API用戶必須處理特定異常的事實是調用方和被調用方之間方法約定的一部分。 合同規定了:被調用者指望的參數的數量和類型,調用者能夠指望的返回值的類型以及調用者但願處理的異常 。
因爲API中不存在使人煩惱的異常,所以只有這些外部異常必須通過檢查 ,才能成爲方法約定的一部分。 相對較少的異常是外生的 ,所以任何API都應具備相對較少的已檢查異常。
受檢查的異常是必須處理的異常。 處理異常就像吞下它同樣簡單。 那裏! 處理異常。 期。 若是開發人員但願以這種方式處理它,那很好。 可是他不能忽視這個例外,並受到警告。
可是,任何檢查過煩人和致命異常的API(例如JCL)都會給API用戶帶來沒必要要的壓力。 這種異常必須處理,但任何的異常是很常見的,它不該該已經擺在首位的異常,或沒有任何東西能夠在搬運時完成。 這致使Java開發人員討厭檢查的異常。
一樣,許多API沒有適當的異常類層次結構,致使全部種類的非外部異常緣由都由單個檢查的異常類(例如IOException
)表示。 這也使Java開發人員討厭檢查的異常。
外在異常是那些不是您的錯,沒法避免且應該處理的異常。 它們構成了全部可能拋出的異常的一小部分。 API應該僅具備檢查的外部異常 ,而全部其餘異常均未選中。 這將使API更好,減輕API用戶的負擔,從而減小捕獲全部,吞下或從新拋出未經檢查的異常的需求。
所以,不要討厭Java及其檢查的異常。 取而代之的是,討厭過分使用已檢查異常的API。