close()
呼び出しを保証するtry-with-resources構文
Java6以前では以下のようにtry-finallyを使って書いていたclose()処理
Closeable r = null;
try {
// リソースを確保
r = getResource();
// リソースを使用
r.use();
} finally {
if (r != null) r.close();
}
ですが、Java7からは以下のような書き方で大変スマートになっています。
// リソースを確保
try (Closeable r = getResource()) {
// リソースを使用
r.use();
}
ご存じ、try-with-resources構文です。
Closeable.close()
はIOException
を投げる
さて上記のコード片ですが、実際のプログラミングをしてみるとある重要な点が省略されていることに気付きます。そう、IOException
が投げ出されるのを処理していないのです。特にtry-with-resources構文を使った後者のコード、getResource()
もuse()
もIOException
を投げないとしても全体としてはIOException
を投げます。なぜなら、糖衣構文で見えていないけれどもclose()
呼び出しがあるから。close()
がIOException
を投げるのでそれを処理する必要があるのです。
つまり後者は実際には、
// リソースを確保
try (Closeable r = getResource()) {
// リソースを使用
r.use();
} catch (IOException e) {
// 適切な処理
}
という形でなくてはいけないのです。もしくはthrows IOException
を付けてさらに上位へ投げるか。どちらにしろ、もともとの触れ込みから期待していたよりも若干ノイズの多いコードになります。
と、ここまではtry-with-resources構文を一度でも使ったことのある方ならすでにお気付きのことかと思います。
IOException
を投げないclose()
を書いたっていい
さてここからが本題。
catch
もthrows
も使うことなく、次のような単純なコードを成立させることもできるんですよというお話。
// リソースを確保
try (MyResource r = getResource()) {
// リソースを使用
r.use();
}
close()
が必ず成功するようなCloseable
/AutoCloseable
クラスというのはありえるわけです。で、そういうクラスを自作する際にCloseable
を実装しつつ
public class MyResource implements Closeable {
public void close() {
// 必ず成功するクローズ処理
}
}
と、throws IOException
なしのclose()
を書くわけです。throws IOException
なしでもインターフェース実装は成立しています。そして、このクラスをリソースとするならば
// リソースを確保
try (MyResource r = getResource()) {
// リソースを使用
r.use();
}
は検査例外を吐かないものとなります。
気が付いてみれば当たり前のことなのですが、IDEを使っていると最初からthrows IOException
が自動的に記述されてしまうため、不要なのに付けっぱなしになり結果として不要なcatch
を強要されてしまうこと、まれによくありそうなので注意喚起まで。