【編者按】作者 Yegor Bugayenko 是 Teamed.io 的軟件架構師,熱衷于軟件質量研究和有效的項目管理方法探索。在本文中,Yegor 就「異常被捕獲但并未重新拋出」這個問題進行了深入討論,并分享了一些建議。

對異常只捕獲但并未重新拋出究竟是 anti-pattern,還是個普通而且非常流行的錯誤確實無從考究。但毫無疑問的是,在所有異常捕獲代碼中,它基本無處不在,正如下面這段代碼:

try {
  stream.write(data);
} catch (IOException ex) {
  ex.printStackTrace();
}

?Catch Me If You Can (2002) by Steven Spielberg 注意:下面的代碼并沒有反對。

try {
  stream.write('X');
} catch (IOException ex) {
  throw new IllegalStateException(ex);
}

這叫做 exception chaining,是一個非常有效的構造。

那么,捕獲異常并記錄究竟存在什么樣的問題?首先,從宏觀著手,這里正在談論的是面向對象編程——意味著需要處理的是對象。一個對象(準確的說,是它的類)應該是這個樣子:

final class Wire {
  private final OutputStream stream;
  Wire(final OutputStream stm) {
   this.stream = stm;
 }
 public void send(final int data) {
    try {
      this.stream.write(x);
    } catch (IOException ex) {
      ex.printStackTrace();
    }
  }
}

這里這樣來應用這個類的:

new Wire(stream).send(1);

看起來不錯,對么?當調用 send(1) 時,并不需要擔心出現 IOException ,它將在方法內部處理。同時,如果出現異常,異常信息會被記錄。但是這么做的理念是完全錯誤的,它傳承自沒有異常處理的語言,比如 C。

異常的發明是為了將整個錯誤處理代碼從主要邏輯中移除,以此來簡化設計。同時,它們不僅僅是被移走,而且被集中在一個地方——在 main() 方法中,即整個應用的入口。

一個異常的主要目的是搜集盡可能多的錯誤信息并將它拋到最上層,在這里用戶能夠針對它做一些處理。Exception chaining 則可以幫助更多,它允許在異常拋至上層的過程中擴充錯誤信息。在這個過程中,實際上非常類似于每次捕獲到泡泡(即異常)后,所做的只是將它添加到一個更大的泡泡中然后重新拋出。當它到達最高層的時候,那里就有許多泡泡,像一個 Russian Doll,一個嵌套著另外一個。最原始的異常就是最小的那個泡泡。

當捕獲一個異常而并沒有重新拋出時,等同于你捏碎了這個泡泡。其中包含的大量信息,包括最原始的異常和所有其它帶有信息的泡泡,都被你牢牢的抓在手中。你杜絕為上層呈現它,同時你如何處理和使用上層也毫無察覺。這一切都像是暗箱操作,一些潛在的重要信息被隱藏。

因此,在這里直接導致的是 send() 方法無法得到信任,同樣基于 send() 方法的操作也無法得到信任,對象之間的信任鏈被破壞殆盡。這里的建議是盡可能少捕獲異常,同時一旦捕獲則必須拋出。

不幸的是,Java 在很多地方的設計違背了這個原則。例如,Java 有需檢查和不需檢查的異常兩類,但是在我看來,只應該有需檢查的異常(這些異常必須被捕獲或者聲明為 throwable)。而且,Java 允許在一個方法中將多個異常類型聲明為 throwable ——這是另一個錯誤;堅持只聲明一種類型。在層次結構的頂部有一個通用的 Exception 類,在我看來也是錯誤的。除此之外,一些內置的類不允許拋出任何需檢查的異常,比如說Runnable.run()Java 還有許多關于異常的問題。

但是嘗試記住這些原則,你的代碼將會更加整潔:catch 除非你別無選擇。

P.S.這個類應該是這個樣子:

final class Wire {
  private final OutputStream stream;
  Wire(final OutputStream stm) {
    this.stream = stm;
  }
  public void send(final int data)
    throws IOException {
    this.stream.write(x);
  }
}

原文鏈接:Catch Me If You ... Can't Do Otherwise

本文系 OneAPM 工程師編譯整理。OneAPM 是應用性能管理領域的新興領軍企業,能幫助企業用戶和開發者輕松實現:緩慢的程序代碼和 SQL 語句的實時抓取。想閱讀更多技術文章,請訪問 OneAPM 官方博客