世間は Java8 に向けて賑わっているというのに周回遅れ感が否めない Java7 ネタ。 Java7 で導入された try-with-resources で C# の using っぽい書き方できるなーと思ってやってみました。
まずは C# の using 。 IDisposable
を実装しているクラスであれば、以下の様な感じで書けますね。
void SomeMethod() { using (var resource = new SomeResource()) { // using のブロックを抜けるタイミングで自動的に resource が開放される } }
こんな感じのを Java でやるには、 AutoCloseable
を実装したクラスであれば以下のように書けます。
void someMethod() { try (SomeResource resource = new SomeResource()) { // try のブロックを抜けるタイミングで自動的に resource が開放される } }
あの煩わしい finally 節での close 処理から解放されてすっきり!と言いたいところですが、このコードはコンパイル通りません。なんでかというと、 AutoCloseable#close()
は Exception
をスローするから。
なので、上記の Java コードは正しくは以下のようにする必要があります*1。
void someMethod() { try (SomeResource resource = new SomeResource()) { // try のブロックを抜けるタイミングで自動的に resource が開放される } catch (Exception e) { // 例外ハンドリング } }
なお、 AutoCloseable#close()
のスローする例外は、 java.lang.Exception
のサブクラスであれば、実装クラスで自由に変更できます。その場合は catch
節ではその例外をキャッチするだけでおk。 catch Exception はバッドプラクティスなので、 AutoCloseable
を実装する場合はスローする例外を明示すべきでしょう。 JDK のクラスでもそのようになっているようです。
それにしても検査例外めんどくさいですね。
待てよ
・・・と、ここまで書いたところで、 RuntimeException
は Exception
のサブクラスだったことを思い出し、「じゃあ close()
で実行時例外投げるように宣言すりゃいいんじゃねえの?」と思ったのでやってみたら catch
なしでもコンパイル通った。
public static void main(String[] args) { try (SomeResource s = new SomeResource()) { // なんか処理 } } public static class SomeResource implements AutoCloseable { @Override public void close() throws RuntimeException { System.out.println("close"); } }
これで良かったのか・・。というか、むしろ実装クラスでは throws
無しでも良い。
*1:throws 節を追加するのでも良い