[英]Exception signature in Java interface
我有以下接口,可以通過多種方式實現。
public interface IStuffManager {
public void createStuff() throws Exception
public class StuffFileManager implements IStuffManager {
public void createStuff() throws IOException {
// file IO
}
由於接口實現,調用代碼無法傳播異常,只能拋出未經檢查的異常:
StuffManager stuffManager = new StuffManager();
try {
stuffManager.createStuff();
} catch (IOException e) {
throw new IllegalStateException(e);
}
拋出異常顯然是一種代碼味道。 怎么修? 調用者可能希望以不同方式處理拋出的異常。
我得到的建議:
這是關於簽名的。
當您在接口中實現方法時,該方法就是實現,而您的 object必須是該接口的直接替代品。 換句話說,如果你寫:
class ArrayList implements java.util.List {
int size() { ... }
}
那么那個size()
方法不僅僅是“你的”尺寸方法。 這是您對List
的size()
方法的實現,因此您必須假設調用者不知道您是 arraylist。也許有人寫了這段代碼:
List myList = whoKnows();
myList.size();
假設whoKnows()
恰好return new ArrayList();
. 上面的代碼沒有想法,如果需要知道,那將是相當糟糕的形式。
這讓我們得到異常簽名:
它們就像它們的名稱一樣是方法性質的一部分。
如果您正在實施size()
,並且這個列表是表示數據庫中表的“虛擬”列表,那么您可能想說:嗯,拋出SQLException
是有意義的。 但是,你不能:有人可能會在不知道你是class DbTableBackedList
的情況下使用你的列表,因此,你不能要求他們處理SQLException
:他們根本沒有,你也做不到,因為也許該代碼早在您創建 class 之前就已編寫。
換句話說,編寫List
接口的size()
方法的實現的部分“交易”是您不拋出SQLException
。
那么當你最終可以扔掉它時你會怎么做?
這讓我們回到檢查異常的本質:它們與名稱一樣是方法簽名的一部分。
這意味着,如果異常是方法本質的基礎,您應該聲明它。 換句話說,這個方法:
public String readFileAsString(File file) {}
按設計壞了。 就其本質而言,它執行 I/O。 因此,編寫此方法的唯一正確方法是將其聲明為throws IOException
。 另一方面,這種方法:
public void saveGame() throws IOException {}
同樣錯誤:保存游戲本質上不受 I/O 限制。 沒有人說游戲一定要保存“到文件”。 它可能會保存到數據庫(在這種情況下, SQLException
更合適),或者保存到雲中,或者保存到 memory,或者誰知道,真的。 除非您記錄saveGame()
方法本質上是 I/O 綁定的,否則通過在其簽名中添加throws IOException
來潛入其本質是沒有意義的。 即使saveGame
的當前實現確實保存到文件。
您可以有 2 個相同的方法:它們都被命名為saveGame
並且它們都有逐行相同的代碼。 而且還是有區別的:一旦版本已經內置到它的骨骼中,它就基本上是文件綁定的; 今天是,明天是,將是永遠,或者至少直到這個庫的重大突破性變化出現。 另一個今天是文件綁定的,但它並沒有融入骨子里:也許明天就不會了,而是基於雲的,或者進入數據庫。
這就是微妙的本性在起作用。 關鍵是,對於接口,接口已經為您定義了這種性質,並且這種性質不涉及在此處顯式拋出 IOException。
因此,您在這里做的事情與您在這個假設的 saveGame 方法中做的事情相同:包裝它,如果需要的話創建您自己的異常。 是的,這意味着您得到 2 個堆棧(您說的是“3”,不知道它從哪里來):您拋出自定義異常,其原因是底層異常。 我不明白那是如何導致 3 疊的; 它導致 2,就像包裝在IllegalStateException
中一樣(假設這與 state 無關,那將是錯誤的做法):
class MyException extends RuntimeException {
public MyException(Throwable cause) {
super(cause);
}
}
...
catch (IOException e) {
throw new MyException(e);
}
這就是你需要做的。
您可以使用一個獨特的額外選項,但我建議您不要這樣做: throw new UncheckedIOException(e);
. 這幾乎是嵌入到核心中的已知異常的唯一“未經檢查”變體,因此它恰好在這里起作用。 但這不太合適,自定義異常似乎更好。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.