簡體   English   中英

Java接口異常簽名

[英]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);
}

拋出異常顯然是一種代碼味道。 怎么修? 調用者可能希望以不同方式處理拋出的異常。

我得到的建議:

  1. 在方法中,捕獲 IOException 並拋出 IllegalStateException(e)
  2. 拋出自定義異常。 這將意味着捕獲時有 3 個異常堆棧,這將很難閱讀。

這是關於簽名的。

當您在接口中實現方法時,該方法就是實現,而您的 object必須是該接口的直接替代品。 換句話說,如果你寫:

class ArrayList implements java.util.List {
  int size() { ... }
}

那么那個size()方法不僅僅是“你的”尺寸方法。 這是您對Listsize()方法的實現,因此您必須假設調用者不知道您是 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM