這篇文章指出了Java中checked Exception的一些缺點,提出應該在程序設計中避免使用checked Exception,對于需要處理checked Exception的代碼,可以使用ExceptionAdapter這個類對checked Exception進行包裝。這篇文章的概念和ExceptionAdapter這個類均源自Bruce Eckel的Does Java need Checked Exception。
Java
的
Exception
分為兩類,一類是
RuntimeException
及其子類,另外一類就是
checked Exception
。
Java
要求函數對沒有被
catch
處理掉的
checked Exception
,需要將其寫在函數的聲明部分。然而,這一要求常常給程序員帶來一些不必要的負擔。
為了避免在函數聲明中寫
throws
部分,在
Java
項目里面常常可以看到以下代碼用來‘吞掉’
Exception
: ?????? try{ ?????????? // ... ?????? }catch(Exceptionex){ ?????????? ex.printStackTrace(); ?????? }
這顯然不是一個好的處理 Exception 辦法,事實上, catch 并處理一個 Exception 意味著讓程序從發生的錯誤 (Exception) 中恢復過來。從這種意義上說,已上的代碼只可能在一些很簡單的情況下工作而不帶來問題。
對于很多 Exception ,往往沒有去處理它并讓程序從錯誤中恢復出來的辦法,這時唯一能做的事情可能就是在界面上顯示一些提示信息給用戶。這種情況下讓程序拋出遇到的 Exception 是更為合理的做法。然而,這樣做會使得一些函數的聲明急劇膨脹。一個函數可能需要聲明會拋出的 7 、 8 個 checked Exception ,而且每個調用它的函數也需要同樣的聲明。
比這更糟糕的是,這有可能破壞類設計的 open-close 原則。簡單來說, open-close 原則是指當擴展一個模塊的時候,可以不影響其現有的 client 。 open-close 原則是通過繼承來實現的,當繼承一個類的時候,我們既擴展了這個類,也不會影響原有的 client (因為對這個類沒有改動)。
現在考慮下面這種情況,有一個父類 Base :
public
class
Base
{
???
???
public
void
foo()
throws
ExceptionA
{
??????
// ...
???
}
}
現在需要繼承
Base
這個類并重載
foo
這個方法,在新的實現中,
foo
可能拋出
ExceptionB
: publicclassExtendextendsBase{ ??? ??? publicvoidfoo()throwsExceptionB{ ?????? // ... ??? } }
然而,這樣寫在 Java 里面是不合法的,因為 Java 把可能會拋出的 Exception 看作函數特征的一部分,子類聲明拋出的 Exception 必須是父類的子集。
可以在 Base 類的 foo 方法中加入拋出 ExceptionB 的聲明,然而,這樣就破壞了 open-close 原則。而且,有時我們沒有辦法去修改父類,比如當重載一個 Jdk 里的類的時候。
另一個可能的做法是在 Extend 的 foo 方法中 catch 住 ExceptionB ,然后構造一個 ExceptionA 并拋出。這是個可行的辦法但也只是一個權宜之計。
如果使用 RuntimeException ,這些問題都不會存在。這說明 checked Exception 并不是一個很實用的概念,也意味著在程序設計的時候,我們應該讓自己的 Exception 類繼承 RuntimeException 而不是 Exception 。(這和 JDK 的建議正好相反,但實踐證明這樣做代碼的質量更好。)
對于那些需要處理
checked Exception
的代碼,可以利用一個
ExceptionAdapter
的類把
checked Exception
包裝成一個
RuntimeException
拋出。
ExceptionAdapter
來自
Bruce Eckel
的
Does Java need Checked Exception
這篇文章,在這里的
ExceptionAdapter
是我根據
JDK 1.4
修改過的:
public
class
ExceptionAdapter
extends
RuntimeException
{
???
???
public
ExceptionAdapter(Exception
ex)
{
??????
super
(ex);
???
}
???
???
public
void
printStackTrace(java.io.PrintStream
s)
{
??????
getCause().printStackTrace(s);
???
}
???
???
public
void
printStackTrace(java.io.PrintWriter
s)
{
??????
getCause().printStackTrace(s);
???
}
???
???
// rethrow()
的作用是把被包裝的
Exception
再次拋出。
???
public
void
rethrow()
??????
throws
Exception
???
{
??????
throw
(Exception)
getCause();
???
}
}
?
參考文獻:
Bruce Eckel -- Does Java need Checked Exception
http://www.mindview.net/Etc/Discussions/CheckedExceptions