try-with-resources を使っても catch が不要になるわけではない・・わけでもなかった

世間は 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 のクラスでもそのようになっているようです。

それにしても検査例外めんどくさいですね。

待てよ

・・・と、ここまで書いたところで、 RuntimeExceptionException のサブクラスだったことを思い出し、「じゃあ 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 無しでも良い。

それ昨日教えてもらってたわー昨日教えてもらってたわー

・・とここまで書いて、ゆとりさんの発言の意味をようやく理解した・・・。

なんかもう Java やっててごめんなさい。

*1:throws 節を追加するのでも良い