JavaGold資格取得に向けた勉強記第22回は「try-with-resources」についてです。
Java 7で導入されたtry-with-resources文は、リソースの自動クローズをサポートする便利な機能です。
この機能を使用することで、ファイル、データベース接続、ネットワークソケットなどのリソースを簡単に管理できます。
この記事では、try-with-resources文の基本的な使い方と注意点について解説します。
try-with-resourcesとは
try-with-resourcesは、Java SE 7で導入された機能で、プログラムの中で扱うリソースを自動的に閉じるためのものです。一般的にプログラムで扱うリソースとは、プログラムからアクセスするデータを指しますが、Javaではデータだけでなく、アクセスするためのインスタンスなどもリソースとして扱います。
リソースは、使う時に開き使い終わったら閉じなければいけません。しかし、「リソースを使い終わったら閉じる」のは任意であって、必須ではないため、プログラマーが閉じ忘れることがあります。リソースを閉じるコードを書かなくてもコンパイルエラーにはならず、正常に処理できてしまうため、テストでも見つかりにくいです。
このようなミスを防ぐために、自動的にリソースを閉じるための構文として導入されたのが、try-with-resourcesです。
try-with-resourcesの基本的な使用方法
AutoCloseableまたはClosableを実装
try-with-resourcesを使用するクラスは、AutoCloseable
またはCloseable
インターフェースを実装する必要があります。どちらもclose
メソッドをもっており、それぞれスローする例外が異なります。
以下で、それぞれのインタフェースの実装例を紹介します。
AutoCloseable
AutoCloseable
インターフェースは、Java 7で導入されました。単一のメソッド close()
を持っており、Exception
型の例外をスローします。
public class MyResource implements AutoCloseable {
@Override
public void close() throws Exception {
System.out.println("Resource closed");
}
}
Closable
close()
メソッドのシグニチャは同じものの、スローする例外クラスが IOException
型となっています。
public class MyCloseableResource implements Closeable {
@Override
public void close() throws IOException {
System.out.println("Resource closed");
}
}
try-with-resourcesの使用
try-with-resourcesでは、tryキーワードの後ろのカッコ内で宣言したリソースが自動的に閉じる対象になります。
try ( 自動的に閉じるリソースの宣言 ){
リソースを使った処理
}
try (ResourceType resource = new ResourceType()) {
resource.performAction();
} catch (Exception e) {
// 例外処理
}
try-with-resourcesで自動的に閉じる対象とするリソースは、tryに続くカッコだけでなく、次のようにtryブロックの前に宣言を記述することも可能です。
ResourceType resource = new ResourceType()
try ( resource ) {
resource.performAction();
} catch (Exception e) {
// 例外処理
}
try-with-resources使用時の注意点
複数のリソースを対象とする場合
なお、複数のリソースを対象とする場合は、セミコロン「;」で区切って列挙します。複数対象とした場合、宣言されたリソースが閉じる順番は、宣言した時の逆の順番です。
public class TestUsing {
public static void main(String[] args) throws Exception {
A a = new A();
try (a;
B b = new B();
C c = new C()) {
// do something
}
}
}
catchブロック、finallyブロックを使用する場合
try-with-resourcesを使う場合でも、catchブロックやfinallyブロックをつけ、例外処理を実行することができます。例外発生の有無に関わらず、リソースはtryブロックを抜けるタイミングで必ず実行されます。
そのため、tryブロック内で例外が発生しなければ、以下の順番で実行されます。
- リソースのクローズ
- finallyブロック
例外が発生すれば、以下の順番で実行されます。
- リソースのクローズ
- catchブロック
- finallyブロック
抑制された例外
Javaのtry-with-resources文やマルチキャッチ機能を使用すると、複数の例外が発生した場合、例外が抑制される可能性があります。抑制された例外とは、tryブロック内で発生した例外と、try-with-resourcesまたはマルチキャッチで発生した例外の両方を処理するために、try-with-resourcesまたはマルチキャッチが抑制(suppressed)する例外のことです。
class MyResource implements AutoCloseable {
@Override
public void close() throws Exception {
System.out.println("Closing the resource");
}
public void doSomething() throws Exception {
System.out.println("Doing something with the resource");
throw new Exception("An exception from doSomething");
}
}
public class Example {
public static void main(String[] args) {
try (MyResource resource = new MyResource()) {
resource.doSomething();
} catch (Exception e) {
System.out.println("Caught exception: " + e.getMessage());
for (Throwable suppressed : e.getSuppressed()) {
System.out.println("Suppressed: " + suppressed.getMessage());
}
}
}
}
この例では、doSomething
メソッド内で例外がスローされ、try-with-resources ブロック内でリソースがクローズされると同時に、クローズ中にも例外が発生します。このとき、try-with-resources ブロックで発生した例外は e
に捕捉され、close
メソッド内で発生した例外は e
の抑制された例外として扱われます。
抑制された例外の取得
Throwable
クラスには、抑制された例外を取得するための getSuppressed
メソッドがあります。上記の例では、e.getSuppressed()
で抑制された例外の配列を取得し、それをループで処理しています。
for (Throwable suppressed : e.getSuppressed()) {
System.out.println("Suppressed: " + suppressed.getMessage());
}
最後に
try-with-resources文は、Javaプログラムでリソースを効率的に管理するための重要な機能です。
リソースのクローズ処理を簡潔に記述し、リソースリークを回避するために、try-with-resources文を積極的に活用しましょう。