<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 1.5-Throwables:簡化異常和錯誤的傳播與檢查 [原文鏈接](http://code.google.com/p/guava-libraries/wiki/ThrowablesExplained)?譯者: 沈義揚 ## 異常傳播 有時候,你會想把捕獲到的異常再次拋出。這種情況通常發生在Error或RuntimeException被捕獲的時候,你沒想捕獲它們,但是聲明捕獲Throwable和Exception的時候,也包括了了Error或RuntimeException。Guava提供了若干方法,來判斷異常類型并且重新傳播異常。例如: ``` try { someMethodThatCouldThrowAnything(); } catch (IKnowWhatToDoWithThisException e) { handle(e); } catch (Throwable t) { Throwables.propagateIfInstanceOf(t, IOException.class); Throwables.propagateIfInstanceOf(t, SQLException.class); throw Throwables.propagate(t); } ``` 所有這些方法都會自己決定是否要拋出異常,但也能直接拋出方法返回的結果——例如,throw Throwables.propagate(t);—— 這樣可以向編譯器聲明這里一定會拋出異常。 Guava中的異常傳播方法簡要列舉如下: | [RuntimeException ? propagate(Throwable)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Throwables.html#propagate%28java.lang.Throwable%29) | 如果Throwable是Error或RuntimeException,直接拋出;否則把Throwable包裝成RuntimeException拋出。返回類型是RuntimeException,所以你可以像上面說的那樣寫成`throw Throwables.propagate(t)`,Java編譯器會意識到這行代碼保證拋出異常。 | |:--- |:--- | | [void propagateIfInstanceOf( Throwable, Class&lt;X extends ? Exception&gt;) throws X](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/…rowables.html#propagateIfInstanceOf%28java.lang.Throwable,%20java.lang.Class%29) | Throwable類型為X才拋出 | | [void propagateIfPossible( Throwable)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Throwables.html#propagateIfPossible%28java.lang.Throwable%29) | Throwable類型為Error或RuntimeException才拋出 | | [void ? propagateIfPossible( Throwable, Class&lt;X extends Throwable&gt;) throws X](http://goo.gl/pgDJC) | Throwable類型為X, Error或RuntimeException才拋出 | ## Throwables.propagate的用法 ### 模仿Java7的多重異常捕獲和再拋出 通常來說,如果調用者想讓異常傳播到棧頂,他不需要寫任何catch代碼塊。因為他不打算從異常中恢復,他可能就不應該記錄異常,或者有其他的動作。他可能是想做一些清理工作,但通常來說,無論操作是否成功,清理工作都要進行,所以清理工作可能會放在finallly代碼塊中。但有時候,捕獲異常然后再拋出也是有用的:也許調用者想要在異常傳播之前統計失敗的次數,或者有條件地傳播異常。 當只對一種異常進行捕獲和再拋出時,代碼可能還是簡單明了的。但當多種異常需要處理時,卻可能變得一團糟: ``` @Override public void run() { try { delegate.run(); } catch (RuntimeException e) { failures.increment(); throw e; }catch (Error e) { failures.increment(); throw e; } } ``` Java7用多重捕獲解決了這個問題: ``` } catch (RuntimeException | Error e) { failures.increment(); throw e; } ``` 非Java7用戶卻受困于這個問題。他們想要寫如下代碼來統計所有異常,但是編譯器不允許他們拋出Throwable(_譯者注:這種寫法把原本是Error或RuntimeException類型的異常修改成了Throwable,因此調用者不得不修改方法簽名_): ``` } catch (Throwable t) { failures.increment(); throw t; } ``` 解決辦法是用throw Throwables.propagate(t)替換throw t。在限定情況下(捕獲Error和RuntimeException),Throwables.propagate和原始代碼有相同行為。然而,用Throwables.propagate也很容易寫出有其他隱藏行為的代碼。尤其要注意的是,這個方案只適用于處理RuntimeException?或Error。如果catch塊捕獲了受檢異常,你需要調用propagateIfInstanceOf來保留原始代碼的行為,因為Throwables.propagate不能直接傳播受檢異常。 總之,Throwables.propagate的這種用法也就馬馬虎虎,在Java7中就沒必要這樣做了。在其他Java版本中,它可以減少少量的代碼重復,但簡單地提取方法進行重構也能做到這一點。此外,使用propagate會意外地包裝受檢異常。 ### 非必要用法:把拋出的Throwable轉為Exception 有少數API,尤其是Java反射API和(以此為基礎的)Junit,把方法聲明成拋出Throwable。和這樣的API交互太痛苦了,因為即使是最通用的API通常也只是聲明拋出Exception。當確定代碼會拋出Throwable,而不是Exception或Error時,調用者可能會用Throwables.propagate轉化Throwable。這里有個用Callable執行Junit測試的范例: ``` public Void call() throws Exception { try { FooTest.super.runTest(); } catch (Throwable t) { Throwables.propagateIfPossible(t, Exception.class); Throwables.propagate(t); } return null; } ``` 在這兒沒必要調用propagate()方法,因為propagateIfPossible傳播了Throwable之外的所有異常類型,第二行的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方法,不然定義這個throwWrappingWeirdThrowable方法也并沒有太大必要。 ## Throwables.propagate的有爭議用法 ### 爭議一:把受檢異常轉化為非受檢異常 原則上,非受檢異常代表bug,而受檢異常表示不可控的問題。但在實際運用中,即使JDK也有所誤用——如Object.clone()、Integer. parseInt(String)、URI(String)——或者至少對某些方法來說,沒有讓每個人都信服的答案,如URI.create(String)的異常聲明。 因此,調用者有時不得不把受檢異常和非受檢異常做相互轉化: ``` 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吧?如果它真的只做了包裝,為什么還非得要寫個方法?”。應該承認,這些問題部分是因為”propagate”的語義太模糊了(用來[拋出未聲明的異常嗎](http://www.eishay.com/2011/11/throw-undeclared-checked-exception-in.html)?)。也許”wrapIfChecked”更能清楚地表達含義。但即使方法叫做”wrapIfChecked”,用它來包裝一個已知類型的受檢異常也沒什么優點。甚至會有其他缺點:也許比起RuntimeException,還有更合適的類型——如IllegalArgumentException。 我們有時也會看到propagate被用于傳播可能為受檢的異常,結果是代碼相比以前會稍微簡短點,但也稍微有點不清晰: ``` } catch (RuntimeException e) { throw e; }catch (Exception e) { throw new RuntimeException(e); } ``` ``` } catch (Exception e) { throw Throwables.propagate(e); } ``` 然而,我們似乎故意忽略了把檢查型異常轉化為非檢查型異常的合理性。在某些場景中,這無疑是正確的做法,但更多時候它被用于避免處理受檢異常。這讓我們的話題變成了爭論受檢異常是不是壞主意了,我不想對此多做敘述。但可以這樣說,Throwables.propagate不是為了鼓勵開發者忽略IOException這樣的異常。 ### 爭議二:異常穿隧 但是,如果你要實現不允許拋出異常的方法呢?有時候你需要把異常包裝在非受檢異常內。這種做法挺好,但我們再次強調,沒必要用propagate方法做這種簡單的包裝。實際上,手動包裝可能更好:如果你手動包裝了所有異常(而不僅僅是受檢異常),那你就可以在另一端解包所有異常,并處理極少數特殊場景。此外,你可能還想把異常包裝成特定的類型,而不是像propagate這樣統一包裝成RuntimeException。 ### 爭議三:重新拋出其他線程產生的異常 ``` try { return future.get(); } catch (ExecutionException e) { throw Throwables.propagate(e.getCause()); } ``` 對這樣的代碼要考慮很多方面: * ExecutionException的cause可能是受檢異常,見上文”爭議一:把檢查型異常轉化為非檢查型異常”。但如果我們確定future對應的任務不會拋出受檢異常呢?(可能future表示runnable任務的結果——_譯者注:如__ExecutorService中的submit(Runnable task, T result)方法_)如上所述,你可以捕獲異常并拋出AssertionError。尤其對于Future,請考慮?[Futures.get](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/util/concurrent/Futures.html#getUnchecked%28java.util.concurrent.Future%29)方法。(TODO:對future.get()拋出的另一個異常InterruptedException作一些說明) * ExecutionException的cause可能直接是Throwable類型,而不是Exception或Error。(實際上這不大可能,但你想直接重新拋出cause的話,編譯器會強迫你考慮這種可能性)見上文”用法二:把拋出Throwable改為拋出Exception”。 * ExecutionException的cause可能是非受檢異常。如果是這樣的話,cause會直接被Throwables.propagate拋出。不幸的是,cause的堆棧信息反映的是異常最初產生的線程,而不是傳播異常的線程。通常來說,最好在異常鏈中同時包含這兩個線程的堆棧信息,就像ExecutionException所做的那樣。(這個問題并不單單和propagate方法相關;所有在其他線程中重新拋出異常的代碼都需要考慮這點) ## 異常原因鏈 Guava提供了如下三個有用的方法,讓研究異常的原因鏈變得稍微簡便了,這三個方法的簽名是不言自明的: | [Throwable ? getRootCause(Throwable)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Throwables.html#getRootCause%28java.lang.Throwable%29) | |:--- |:--- | | [List&lt;Throwable&gt; ? getCausalChain(Throwable)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Throwables.html#getCausalChain%28java.lang.Throwable%29) | | [String ? getStackTraceAsString(Throwable)](http://docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/…ogle/common/base/Throwables.html#getStackTraceAsString%28java.lang.Throwable%29) |
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看