Java7 try-with-resourcesのsuppressed exceptionsを検証
前エントリでちょこっと書いたJava7 try-with-resourcesのsuppressed exceptionsを検証する。
public class Test implements AutoCloseable{ private int index; public Test(int index) { super(); this.index = index; } public static void main(String[] args) throws IOException, SQLException { try (Test test = new Test(1); Test test2 = new Test(2)) { test.throwSQLException(); } catch (IOException e) { System.err.println("IOException occurred."); throw e; } catch (SQLException e) { System.err.println("SQLException occurred."); throw e; } } public void throwSQLException() throws SQLException { System.err.println("throwSQLException:"+index); throw new SQLException("sql error:"+index); } @Override public void close() throws IOException { System.err.println("close:"+index); throw new IOException("close error:"+index); } }
まず面白かったのがAutoCloseable#closeでスローされた例外もtry-with-resourcesのcatchで補足できること。AutoClosebale#closeはthrows Exceptionと定義されているのだが、サブクラスではthrowsをもっと具象的な例外に定義しなおすことが出来る。Java7コンパイラはtryで宣言されている変数の型もちゃんとチェックしていて、closeが投げる例外(上記コードではIOException)が検査例外の場合はcatchするかthrowsしないとコンパイルエラーとなる。
上記コードを実行してみた。
throwSQLException:1 close:2 close:1 SQLException occurred. Exception in thread "main" java.sql.SQLException: sql error:1 at Test.throwSQLException(Test.java:30) at Test.main(Test.java:18) Suppressed: java.io.IOException: close error:2 at Test.close(Test.java:36) at Test.main(Test.java:19) Suppressed: java.io.IOException: close error:1 at Test.close(Test.java:36) at Test.main(Test.java:19)
- まずthrowSQLExceptionが実行されSQLExceptionがスローされるが、この時点ではまだcatchされない。
- 次にTest#closeがtry中宣言の逆順に呼ばれている。それぞれのclose内ではIOExceptionがスローされているが、これらはサプレス(抑制?)されて先にスローされたSQLExceptionに格納されているので、最後までcatchで補足されない。
- 最後にSQLExceptionがcatchされる。
スタックトレースを見るとちゃんとサプレスされたIOExceptionが2つ入っているのが分かる。これらはThrowable#getSuppressedメソッドで取得することも出来る。
次にtry{...}中で例外がスローされなかった場合も見てみよう。throwSQLException呼び出しをコメントアウトする。ついでにTestインスタンスを一つ増やした。
public class Test implements AutoCloseable{ private int index; public Test(int index) { super(); this.index = index; } public static void main(String[] args) throws IOException, SQLException { try (Test test = new Test(1); Test test2 = new Test(2); Test test3 = new Test(3)) { // test.throwSQLException(); } catch (IOException e) { System.err.println("IOException occurred."); throw e; } } public void throwSQLException() throws SQLException { System.err.println("throwSQLException:"+index); throw new SQLException("sql error:"+index); } @Override public void close() throws IOException { System.err.println("close:"+index); throw new IOException("close error:"+index); } }
結果↓
close:3 close:2 close:1 IOException occurred. Exception in thread "main" java.io.IOException: close error:3 at Test.close(Test.java:34) at Test.main(Test.java:20) Suppressed: java.io.IOException: close error:2 ... 2 more Suppressed: java.io.IOException: close error:1 ... 2 more
closeが逆順に呼ばれるまでは一緒。最初に発生したIOExceptionがcatchされた。catchされなかった例外は全てcatchされた例外にsuppressed exceptionsとして格納されている。