BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ ニュース Java の自動リソース管理

Java の自動リソース管理

原文(投稿日:2010/08/23)へのリンク

Project Coin には,自動リソース管理 (Automatic Resource Management, ARM) を行う機能がある。コードブロックのエラー時や正常終了時にクローズが必要な,外部リソースの操作を簡単にすることが目的だ。次のような単純なファイルコピー操作を考えてみよう。Java Bytestream チュートリアル から引用したものだ。

FileInputStream in = null;
FileOutputStream out = null;
try {
  in = new FileInputStream("xanadu.txt");
  out = new FileOutputStream("outagain.txt");
  int c;
  while ((c = in.read()) != -1)
    out.write(c);
} finally {
  if (in != null)
    in.close();
  if (out != null)
    out.close();
}

ボイラープレート(定形的な処理)がたくさんあるが,それだけではない。資料によると InputStream.Close() では IOException がスローされる可能性があるのだ。(例外の発生する可能性は OutputStream の方がはるかに大きい。それでもこのコードを正しくコンパイルするためには,外部に catch ブロックを置くか,あるいは例外伝播(propagation)を宣言する必要がある。)

さらに try-catch-finally ブロックの構文スコープの問題から,FileInputStream 変数 in と FileOutputStream 変数 out は構文上,ブロック自体の外部で定義する必要がある。(try ブロック内部で定義すると,catch あるいは finally ブロックから参照できなくなる。)

このようなボイラプレートコードを排除して,ブロック内で使用されるリソースの構文スコープを強固なものにするために,Java 言語の try ブロックに新たな仕様が追加されることになった。この try-with-resource ブロック (または ARM ブロック) の 初期仕様(initial specification)初期実装(initial implementation) を通じて提供され,JDK7 の ビルド105 でも採用されている。

新たなインターフェイスとして java.lang.AutoClosable提案API (proposed API) に追加された。これには Exception をスローする close() メソッドがひとつ定義されている。このインターフェースは java.io.Closeable の親として組み込まれてるので,すべての InputStreamOutputStream がこの処理の恩恵を受けられる。さらに FileLockImageInputStream にも AutoCloseable インターフェースが適用されている。

これを使えば,先の例は次のように書き直すことができる。

try (
  FileInputStream in = new FileInputStream("xanadu.txt");
  FileOutputStream out = new FileOutputStream("outagain.txt")
) {
  int c;
  while((c=in.read()) != -1 )
    out.write();
}

try ブロックの最後に達すると,正常終了かどうかに関わらず outin 両方のリソースに対してclose() が自動的にコールされる。さらに最初の例とは違い,out.close()in.close() が共に実行されることも保証される。(最初の例では in.close() が例外をスローした場合,その次の out.clse() は実行されない)

この例には小さいが,しかし注目に値する特徴がいくつも存在する。

  • 現時点では,リソースセクションの最後のリソースの後にセミコロン(';')を付加することはできない。
  • リソースブロックを try 本体から分離するため,通常の {} の代わりに () を使用する。またリソースブロックがある場合には,1つ以上のリソースを定義する必要がある。
  • 各リソース定義は type var = expression の形式でなければならない。また,これ以外の一般式をリソースブロックに記述することはできない。
  • リソースは暗黙的に final である。つまり final 修飾子が存在するように動作する。リソース変数への値の設定はすべて,コンパイル時にエラーとなる。
  • リソースは AutoCloseable のサブタイプでなければならない。この条件を満たさない場合はコンパイル時にエラーとなる。
  • クローズ処理の実行順序は,リソース定義と逆順である。先程の修正後の例で言えば,in.close() の前に out.close() がコールされる。これによって,ネストしたストリームを外から中へと構築し,その逆順でクローズすることが可能になるため,定義順に実行するよりも理にかなっている (例えば,下位のストリームがクローズされる前にバッファをフラッシュできる)。
  • n 個のリソースを使用するブロックでは,n+1 個の例外が発生する可能性がある。これは処理ブロック本体で例外がスローされて,各リソースのクローズ処理でも例外が発生する時に起こる。この場合,本体の例外は実際にスローされるが,その他は例外クラスの 抑制例外リスト(suppressed exception list) に追加される。このリストの内容には getSuppressedExceptions() メソッドを通じてアクセスできる。
  • Exception クラスから取得するスタックトレースには Suppressed: というプレフィックスが付加されている場合がある。シリアライズされた Throwable のフォーマットも同様だ。(これは Java 6 クライアントから Java 7 ランタイム上のリモートサービスを起動する場合,あるいはその逆の場合に影響する可能性がある。)
  • 現時点では javax.swingjava.sql は ARM に適合していない。ARM で使用するクラスは AutoCloseable を継承するものに限られているためだ。JDBC 4.1 が JDK 7 に含まれれば ARM がサポートされるはずだが,それがいつになるかは明確になっていない。

Java 開発者のワークフローからボイラープレートコードを除去することで,いくらかの生産性向上は達せられるだろう。しかし JDK 7 で採用されるとはいえ,これを利用したコードが記述できるようになるには,まだ多少の時間がかかりそうだ。Java 6 で実行させるためには,コンパイル時に多くのライブラリが必要になる。自動リソース管理が適用されるのは,-target 7 (または同種の) オプション指定でコンパイルされたコードに限定されるだろう。Java 6 の寿命が尽き,Java 8 がリリースされた頃には,自動的に ARM の使用が有効になっているかも知れない。

この記事に星をつける

おすすめ度
スタイル

BT