用Guava輔助Throwable異常處理

  Guava的 Throwables 工具經常可讓exception處理更方便。

  Propagation

  有時候,你會想把捕獲的exception拋到上一個try/catch塊。對於 RuntimeException 和 Error 尤其如此,它們不須要 try/catch 塊,但可能被其餘的 try/catch 塊無心捕獲。

  Guava 提供了一些工具來簡化propagate exception。例如:

  try{

  someMethodThatCouldThrowAnything();

  }catch(IKnowWhatToDoWithThisException e){

  handle(e);

  }catch(Throwable t){

  Throwables.propagateIfInstanceOf(t, IOException.class);

  Throwables.propagateIfInstanceOf(t, SQLException.class);

  throw Throwables.propagate(t);

  }

  這裏的每一個方法都會拋出它們本身的exception,可是throw最終結果 —— 如,throw Throwables.propagate(t) —— 對編譯檢查有指示做用,提示這裏確定會拋出一個exception。

  這裏是Guava提供的propagation方法的簡單總結:

  參數形式解釋

  RuntimeException propagate(Throwable)若是參數throwable是 RuntimeException 或 Error 則原樣propagate,不然將它包入 RuntimeException 中拋出。保證拋出。返回值是 RuntimeException 類型,所以你能夠像上面那樣寫 throw Throwables.propagate(t) ,這樣Java編譯器會明白這一行確定會拋出一個exception。

  void propagateIfInstanceOf(Throwable, Class) throws X僅當參數throwable是 X 類型時,原樣propagate。

  void propagateIfPossible(Throwable)僅當參數throwable是 RuntimeException 或 Error 類型時,原樣propagate。

  void propagateIfPossible(Throwable, Class) throws X僅當參數 throwable 是 RuntimeException 或 Error 或 X 類型時,原樣propagate。

  Throwables.propagate的用途 模仿Java 7的多重catch和從新throw

  通常來講,調用者若是想要讓exception沿着調用棧傳播,他只要不寫 catch 塊就能夠了。既然他不打算在exception後恢復,他恐怕也不須要寫入log或者採起什麼其餘行動。他可能須要進行一些清理工做,可是不管有沒有expction都會須要,所以清理工做會放在 finally 塊中。儘管如此,會從新throw的 catch 塊有時仍是有意義的:也許調用者想要在傳播exception以前先更新崩潰計數器,或者他只想在特定條件下傳播exception。

  只有一種exception的時候,catch和從新throw是簡單直接的。麻煩的是有多種exception的時候:

  @Overridepublicvoid run(){

  try{

  delegate.run();

  }catch(RuntimeException e){

  failures.increment();

  throw e;

  }catch(Error e){

  failures.increment();

  throw e;

  }

  }

  Java 7用 multicatch 來解決這個問題:

  }catch(RuntimeException|Error e){

  failures.increment();

  throw e;

  }

  但不用Java 7的用戶就沒辦法了。他們也想寫以下的代碼,但編譯器不容許拋出Throwable類型的變量:

  }catch(Throwable t){

  failures.increment();

  throw t;

  }

  解決方案是用 throw Throwables.propagate(t) 來替換 throw t 。僅就這種狀況而言, Throwables.propagate 跟以前代碼的功能徹底相同。可是,代碼裏寫 Throwables.propagate 很容易有一種隱藏的反作用。具體來講,要注意上面這種模式只適用於 RuntimeException 和 Error。若是 catch 塊可能捕捉checked exception,你還須要調用 propagateIfInstanceOf 來保證正常功能,由於 Throwables.propagate 沒法直接傳播checked exception。

  整體來講,這種 propagate 的用法效果通常。在Java 7下不必這樣作。在其餘版本的Java下,這樣能略微減小重複,可是一個簡單的Extract Method重構也能夠達到一樣效果。另外,使用 propagate makes it easy to accidentally wrap checked exceptions.

  無用功:把 throws Throwable 轉化爲 throws Exception

  某些API,尤爲是Java reflection API 和 (相應的) JUnit,有拋出 Throwable 的方法。運用這些API可能很麻煩,由於就算是最通用的API通常也只聲明throws Exception。 Throwables.propagate 是爲非 Exception ,非 Error 的 Throwable 準備的。這個例子聲明瞭一個執行JUnit測試的 Callable :

  public void call() throws Exception{

  try{

  FooTest.super.runTest();

  }catch(Throwable t){

  Throwables.propagateIfPossible(t,Exception.class);

  Throwables.propagate(t);

  }

  return null;

  }

  這裏不必propagate(),而且第二行與="throw new RuntimeException(t)"等價。 (順便說一句:這個例子也提醒了我 propagateIfPossible 可能使人迷惑,由於它不只傳播參數指定的類型,也傳播 Error 和 RuntimeException。)

  這種模式 (或者相似的寫法,如"throw new RuntimeException(t)") 在Google的代碼庫裏至少出現 30 次。(搜索'propagateIfPossible[^;]* Exception.class[)];'試試。) 採用"throw new RuntimeException(t)"寫法的略佔多數。咱們也有可能想要一個"throwWrappingWeirdThrowable"方法來作Throwable到Exception的轉換,可是既然兩行就能搞定,這個方法仍是沒有太大必要,除非咱們要廢除propagateIfPossible方法 www.yztrans.com

  Throwables.propagate有爭議的用法 有爭議:把 checked exception 轉化爲 unchecked exception

  理論上,unchecked exception表示有bug,checked exceptions表示在你控制範圍以外的問題。但在實踐上,即便JDK有時也會用錯 (至少,對於某些方法,沒有廣泛認同的正確答案)。

  所以,有時調用者須要讓這兩種exception類型相互轉化:

  try{

  return Integer.parseInt(userInput);

  }catch(NumberFormatException e){

  throw new InvalidInputException(e);

  }

  try{

  return publicInterfaceMethod.invoke();

  }catch(IllegalAccessException e){

  throw new AssertionError(e);

  }

  有時候,這些調用者會用 Throwables.propagate 。有什麼壞處呢?最主要的問題是代碼的含義不太明顯。throw Throwables.propagate(ioException) 有什麼效果?throw new RuntimeException(ioException) 有什麼效果?這兩行代碼功能是相同的,但後者更直白。前者令人生疑:"這是在作什麼?應該不僅是打包成 RuntimeException 吧?若是隻爲這個,爲什麼不寫一個wrapper方法呢?"只能認可,部分問題在於"propagate"這個名字很含糊。(它是一個拋出未聲明exception的方式嗎?) 也許換成"wrapIfChecked"會好一些。但即便叫這個名字,在已知checked exception上調用它也沒有什麼優點。可能還會有反作用: 也許會有比普通 RuntimeException 更合適的拋出類型 -- 好比, IllegalArgumentException。

  咱們有時也會看到在exception僅僅有多是checked exception時用 propagate 。相對來講,這種作法後果小一些,也不那麼直白:

  }catch(RuntimeException e){

  throw e;

  }catch(Exception e){

  throw new RuntimeException(e);

  }

  }catch(Exception e){

  throw Throwables.propagate(e);

  }

  儘管如此,這裏不可忽視的問題在於將checked exception轉化爲unchecked exception的行爲自己。在某些狀況下是無可厚非的,但更多的時候這樣作是在逃避對常規 checked exception 的處理。這讓咱們思考checked exception自己是否就是一個壞主意。我不想說得這麼深刻。姑且說 ,Throwables.propagate 不是讓 Java 使用者用來忽略 IOException 及相似異常的。

  有爭議: Exception隧道

  可是當你實現一個不容許throw exception的方法時怎麼辦呢?有時你須要把exception打包在unchecked exception中。這樣沒問題,但一樣的,對於單純的打包 propagate 沒有必要。事實上,本身實現打包會更好一些:若是你把每一個exception(而不僅是checked exception)打包,那就能夠在另外一端解包,減小特殊處理。另外,最好打包成自定義的exception類型。

  有爭議: 從新拋出其餘線程的exception

  try{

  return future.get();

  }catch(ExecutionException e){

  throw Throwables.propagate(e.getCause());

  }

  這裏有幾件要考慮的事:

  對於checked exception:參見上面的"把 checked exception 轉化爲 unchecked exception"一節。但若是此處已知不會拋出checked exception呢? (也許是 Runnable 的結果。) 如上所述,你能夠catch這個exception而後拋出 AssertionError ;propagate 沒有什麼用。特別的是,對於Future,也能夠考慮Futures.get。

  對於非Exception,非Error的Throwable。(好吧,這個可能性不大,可是若是你試圖直接從新throw它,編譯器會強迫你考慮這種可能) 參考上面的 "把 throws Throwable 轉化爲 throws Exception" 一節.

  對於unchecked exception或error。那麼它會直接被從新throw。不幸的是,它的stack trace會顯示原先產生exception的線程,而不是當前傳播到的線程。通常最好的結果是把兩個線程的stack trace都包括在exception chain中,就像 get 拋出的 ExecutionException 同樣。(這其實不是 propagate 的問題; 這是全部把 exception 在另外一個線程拋出的共同問題。)

  因果鏈

  Guava讓研究一個exception的因果鏈(causal chain)更簡單了一些,它提供瞭如下3個頗有用的方法,功能顧名思義:

  Throwable getRootCause(Throwable)

  List getCausalChain(Throwable)

  String getStackTraceAsString(Throwable) www.lefeng123.com  託福答案

 app

相關文章
相關標籤/搜索