mike-neckのブログ

JavaかJavaFXかJavaEE(なんかJava8が多め)

try-with-resources に複数のリソースを指定した場合の close の実行順

年末年始に書いていたコードで気になったことがあったので調べた。

f:id:mike_neck:20181019112748p:plain

結論だけ先に書くと、 try-with-resources に選択したリソースの順番の逆順になる。


Java9 以降で使えるようになった effevtively-final variables in try-with-resources の実行順がどうなるのかちゃんと理解していなかったが、 Java 言語仕様には書かれていた。

引用すると次の箇所

Resources are initialized in left-to-right order. ...

リソースは左から右の順番に初期化される

Resources are closed in the reverse order from that in which they were initialized. ... An exception from the closing of one resource does not prevent the closing of other resources.

リソースは初期化とは逆順にクローズされる。クローズ時に例外が発生した場合でも他のリソースのクローズに支障をきたさない

ここで、初期化すでにしているのではないかと思うのだが、続く 14.20.3.1 を読んで try-with-resources スコープの中での初期化のことなのかと思ったのだが誤解しているかもしれない。

14.20.3.1 の内容を一部だけ書くと、

try (autoCloseable) {

}

となっていたものは、一度次のような形に変換されるらしい。

try (final T resource = autoCloseable) {

}

というわけで、次のコードを jshell で動かして実験する。

try にて指定するリソースに 0 からの順番に名前をつける。一方、クローズする順番を 0 から数えていく。また、 4 番目(名前は 3)のリソースのクローズ時に例外を発生させる

この時に、仕様通りであれば、

int[] count = new int[] { 0 };

AutoCloseable autoCloseable(int num) {
  return () -> {
    System.out.println("num: " + num + " -> " + count[0]++);
    if (num % 4 == 3) {
      throw new Exception("num: " + num);
    }
  };
}

try {
  final AutoCloseable a0 = autoCloseable(0);
  final AutoCloseable a1 = autoCloseable(1);
  final AutoCloseable a2 = autoCloseable(2);
  final AutoCloseable a3 = autoCloseable(3);
  final AutoCloseable a4 = autoCloseable(4);
  try (a0; a1; a2; a3; a4) {
    System.out.println("action in try");
  } catch (final Exception e) {
    System.out.println(e.getMessage());
    throw e;
  }
} catch (final Exception e) {
  System.out.println("outer: " + e.getMessage());
} finally {
  System.out.println("finish");
}

これの出力結果は次のとおりとなった。

action in try
num: 4 -> 0
num: 3 -> 1
num: 2 -> 2
num: 1 -> 3
num: 0 -> 4
num: 3
outer: num: 3
finish

結論

try(r0;r1;r2 ...) と複数のリソースを指定した場合のクローズ実行順は逆順、つまり r2, r1, r0 の順番になる