2013年6月20日

try-with-resources

Java SE 7 の新機能に「The try-with-resources Statement」というものがあります。

これまではリソースをオープンした場合、このように忘れずにclose()しないといけませんでした。
SomeIO io = null;
try {
    io = new SomeIO();
    io.somethingIO();
} finally {
    if (io != null) {
        io.close();
    }
}

毎回同じようなコードを書くのは面倒だ!
ということで作成された今回の新機能。tryを書くときに宣言すると、ブロックを抜ける場合にclose()を呼んでくれるステキな機能です。
try (SomeIO io = new SomeIO()) {
    io.somethingIO();
}
これだけでOKとは本当にステキです。

ここで指定できるクラスは java.lang.AutoCloseable を実装したクラスです。
ここではわざとIOExceptionを発生させるようにしています。
class SomeIO implements AutoCloseable {
    @Override
    public void close() throws IOException {
        System.out.println("忘れずにClose!");
    }

    public void somethingIO() throws IOException {
        System.out.println("IOが発生!");
        throw new IOException("IO Error!");
    }
}

実行結果をみると、例外が発生していますが、close()してくれています。
IOが発生!
忘れずにClose!
Exception in thread "main" java.io.IOException: IO Error!
 at TryWithResources$SomeIO.somethingIO(TryWithResources.java:24)
 at TryWithResources.main(TryWithResources.java:9)

しかし周回遅れエンジニアは性格が悪いので、SomeIOをこんな風にclose()時にもIOExceptionを発生させてしまいます。
class SomeIO implements AutoCloseable {
    @Override
    public void close() throws IOException {
        System.out.println("忘れずにClose!");
        throw new IOException("Close時にIO Error!");
    }

    public void somethingIO() throws IOException {
        System.out.println("IOが発生!");
        throw new IOException("IO Error!");
    }
}

最初の従来ソースでこれを実行すると、somethingIO()にて発生した例外が握りつぶされてしまいます。
Exception in thread "main" IOが発生!
忘れずにClose!
java.io.IOException: closeに失敗!
 at TraditionalIO$SomeIO.close(TraditionalIO.java:31)
 at TraditionalIO.main(TraditionalIO.java:18)

そのため、finally区内でもtry-catchを書く必要がありました。リソースが増えるとその分、try-catchが増えるといったイヤラシイ定型句になっていました。
SomeIO io = null;
try {
    io = new SomeIO();
    io.somethingIO();

} finally {
    if (io != null) {
        try {
            io.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

こんどはスタックトレースがしっかり出力されています。
IOが発生!
忘れずにClose!
java.io.IOException: closeに失敗!
 at TraditionalIO$SomeIO.close(TraditionalIO.java:31)
 at TraditionalIO.main(TraditionalIO.java:18)
Exception in thread "main" java.io.IOException: IOに失敗!
 at TraditionalIO$SomeIO.somethingIO(TraditionalIO.java:36)
 at TraditionalIO.main(TraditionalIO.java:11)


では、try-with-resources句を使うとどういった実行結果となるでしょうか?
IOが発生!
忘れずにClose!
Exception in thread "main" java.io.IOException: IO Error!
 at TryWithResources$SomeIO.somethingIO(TryWithResources.java:24)
 at TryWithResources.main(TryWithResources.java:9)
 Suppressed: java.io.IOException: Close時にIO Error!
  at TryWithResources$SomeIO.close(TryWithResources.java:19)
  at TryWithResources.main(TryWithResources.java:10)
なんということでしょう。
周回遅れエンジニアのように性格の悪い人がいたのか、しっかり「Suppressed」としてスタックトレースから確認することができました。
こちらは java.lang.Throwable の拡張のようで、Throwable.addSuppressed()や、Throwable.getSuppressed()にて追加・取得ができるようです。



この機能によってclose()のし忘れを防止でき、コードもシンプルになりました。
これは使うしかないですね。

0 コメント:

コメントを投稿