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 の親として組み込まれてるので,すべての InputStream と OutputStream がこの処理の恩恵を受けられる。さらに FileLock と ImageInputStream にも AutoCloseable インターフェースが適用されている。
これを使えば,先の例は次のように書き直すことができる。
try (
FileInputStream in = new FileInputStream("xanadu.txt");
FileOutputStream out = new FileOutputStream("outagain.txt")
) {
int c;
while((c=in.read()) != -1 )
out.write();
}
try ブロックの最後に達すると,正常終了かどうかに関わらず out と in 両方のリソースに対して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.swingとjava.sqlは ARM に適合していない。ARM で使用するクラスはAutoCloseableを継承するものに限られているためだ。JDBC 4.1 が JDK 7 に含まれれば ARM がサポートされるはずだが,それがいつになるかは明確になっていない。
Java 開発者のワークフローからボイラープレートコードを除去することで,いくらかの生産性向上は達せられるだろう。しかし JDK 7 で採用されるとはいえ,これを利用したコードが記述できるようになるには,まだ多少の時間がかかりそうだ。Java 6 で実行させるためには,コンパイル時に多くのライブラリが必要になる。自動リソース管理が適用されるのは,-target 7 (または同種の) オプション指定でコンパイルされたコードに限定されるだろう。Java 6 の寿命が尽き,Java 8 がリリースされた頃には,自動的に ARM の使用が有効になっているかも知れない。