Java SE 7 (2) - リソース自動closeの仕組み
文字列switchに続いて、バイトコードに影響する言語仕様の2つ目、"The try-with-resources Statement"です。
これまでは下のように書いていたものが、
InputStream is = new FileInputStream("a.txt"); try { // TODO } finally { is.close(); }
こう書けるようになりました。
try (InputStream is = new FileInputStream("a.txt")) { // TODO }
スコープはtryブロックに閉じるので、同じ変数名で並べることができます。
try (InputStream is = new FileInputStream("a.txt")) { // TODO } try (InputStream is = new FileInputStream("a.txt")) { // TODO }
また、複数のリソースを同時に書けます。でも、区切り文字が";"なのはちょっとかっこ悪い気が。ここは","じゃない?
try (InputStream is = new FileInputStream("in.txt"); OutputStream os = new FileOutputStream("out.txt")) { // TODO }
これらは、Java1.7で導入されたAutoCloseable*1インタフェースを実装しているクラスならどれでも使うことが出来ます。今回はCloseableと違い、InputStreamやOutputStreamだけでなくJDBCのConnection,Statement,ResultSetにも適用されています。
もちろん自作クラスにも使えます。
以下の例は、前回と同様にそれぞれJava7とJava6(とそれ以前)で書いた2つのプログラムのバイトコードが同じになるように書いてみたものです。f1がJava7版、f2がJava6版です。
複数リソース版も併記します。
(念のため:コンパイラによっては生成されるコードが異なる可能性があります。今回は本家のJDKを使っています。)
追記@2011-09-10:Throwable#addSuppressedは1.7で追加されたメソッドなので、厳密には1.6ではコンパイルできません。うっかりしてました。
以前から、try-catch-finallyは裏側ではコードが大量生成されていたわけですが、これも元のコードと比べるとかなり大量のバイトコードが生成されていることが分かります。
単一リソース版
final class BogusResource implements AutoCloseable { int read() throws Exception { return 0; } public void close() throws Exception { } public static void f1() throws Exception { try (BogusResource resource = new BogusResource()) { resource.read(); } } public static void f2() throws Exception { BogusResource resource = new BogusResource(); Throwable th = null; try { resource.read(); } catch (Exception ex) { th = ex; throw ex; } finally { if (resource != null) { if (th != null) { try { resource.close(); } catch (Exception ex) { th.addSuppressed(ex); } } else { resource.close(); } } } } }
Compiled from "2a.java"
final class BogusResource implements java.lang.AutoCloseable {
BogusResource();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
int read() throws java.lang.Exception;
Code:
0: iconst_0
1: ireturn
public void close() throws java.lang.Exception;
Code:
0: return
public static void f1() throws java.lang.Exception;
Code:
0: new #2 // class BogusResource
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_0
8: aconst_null
9: astore_1
10: aload_0
11: invokevirtual #4 // Method read:()I
14: pop
15: aload_0
16: ifnull 84
19: aload_1
20: ifnull 39
23: aload_0
24: invokevirtual #5 // Method close:()V
27: goto 84
30: astore_2
31: aload_1
32: aload_2
33: invokevirtual #7 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
36: goto 84
39: aload_0
40: invokevirtual #5 // Method close:()V
43: goto 84
46: astore_2
47: aload_2
48: astore_1
49: aload_2
50: athrow
51: astore_3
52: aload_0
53: ifnull 82
56: aload_1
57: ifnull 78
60: aload_0
61: invokevirtual #5 // Method close:()V
64: goto 82
67: astore 4
69: aload_1
70: aload 4
72: invokevirtual #7 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
75: goto 82
78: aload_0
79: invokevirtual #5 // Method close:()V
82: aload_3
83: athrow
84: return
Exception table:
from to target type
23 27 30 Class java/lang/Throwable
10 15 46 Class java/lang/Throwable
10 15 51 any
60 64 67 Class java/lang/Throwable
46 52 51 any
public static void f2() throws java.lang.Exception;
Code:
0: new #2 // class BogusResource
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_0
8: aconst_null
9: astore_1
10: aload_0
11: invokevirtual #4 // Method read:()I
14: pop
15: aload_0
16: ifnull 84
19: aload_1
20: ifnull 39
23: aload_0
24: invokevirtual #5 // Method close:()V
27: goto 84
30: astore_2
31: aload_1
32: aload_2
33: invokevirtual #7 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
36: goto 84
39: aload_0
40: invokevirtual #5 // Method close:()V
43: goto 84
46: astore_2
47: aload_2
48: astore_1
49: aload_2
50: athrow
51: astore_3
52: aload_0
53: ifnull 82
56: aload_1
57: ifnull 78
60: aload_0
61: invokevirtual #5 // Method close:()V
64: goto 82
67: astore 4
69: aload_1
70: aload 4
72: invokevirtual #7 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
75: goto 82
78: aload_0
79: invokevirtual #5 // Method close:()V
82: aload_3
83: athrow
84: return
Exception table:
from to target type
23 27 30 Class java/lang/Exception
10 15 46 Class java/lang/Exception
10 15 51 any
60 64 67 Class java/lang/Exception
46 52 51 any
}
複数リソース版
final class BogusResource implements AutoCloseable { int read() throws Exception { return 0; } public void close() throws Exception { } public static void f1() throws Exception { try (BogusResource resource1 = new BogusResource(); BogusResource resource2 = new BogusResource()) { resource1.read(); resource2.read(); } } public static void f2() throws Exception { BogusResource resource1 = new BogusResource(); Throwable th1 = null; try { BogusResource resource2 = new BogusResource(); Throwable th2 = null; try { resource1.read(); resource2.read(); } catch (Exception ex) { th2 = ex; throw ex; } finally { if (resource2 != null) { if (th2 != null) { try { resource2.close(); } catch (Exception ex) { th2.addSuppressed(ex); } } else { resource2.close(); } } } } catch (Exception ex) { th1 = ex; throw ex; } finally { if (resource1 != null) { if (th1 != null) { try { resource1.close(); } catch (Exception ex) { th1.addSuppressed(ex); } } else { resource1.close(); } } } } }
Compiled from "2b.java"
final class BogusResource implements java.lang.AutoCloseable {
BogusResource();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
int read() throws java.lang.Exception;
Code:
0: iconst_0
1: ireturn
public void close() throws java.lang.Exception;
Code:
0: return
public static void f1() throws java.lang.Exception;
Code:
0: new #2 // class BogusResource
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_0
8: aconst_null
9: astore_1
10: new #2 // class BogusResource
13: dup
14: invokespecial #3 // Method "<init>":()V
17: astore_2
18: aconst_null
19: astore_3
20: aload_0
21: invokevirtual #4 // Method read:()I
24: pop
25: aload_2
26: invokevirtual #4 // Method read:()I
29: pop
30: aload_2
31: ifnull 106
34: aload_3
35: ifnull 56
38: aload_2
39: invokevirtual #5 // Method close:()V
42: goto 106
45: astore 4
47: aload_3
48: aload 4
50: invokevirtual #7 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
53: goto 106
56: aload_2
57: invokevirtual #5 // Method close:()V
60: goto 106
63: astore 4
65: aload 4
67: astore_3
68: aload 4
70: athrow
71: astore 5
73: aload_2
74: ifnull 103
77: aload_3
78: ifnull 99
81: aload_2
82: invokevirtual #5 // Method close:()V
85: goto 103
88: astore 6
90: aload_3
91: aload 6
93: invokevirtual #7 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
96: goto 103
99: aload_2
100: invokevirtual #5 // Method close:()V
103: aload 5
105: athrow
106: aload_0
107: ifnull 177
110: aload_1
111: ifnull 130
114: aload_0
115: invokevirtual #5 // Method close:()V
118: goto 177
121: astore_2
122: aload_1
123: aload_2
124: invokevirtual #7 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
127: goto 177
130: aload_0
131: invokevirtual #5 // Method close:()V
134: goto 177
137: astore_2
138: aload_2
139: astore_1
140: aload_2
141: athrow
142: astore 7
144: aload_0
145: ifnull 174
148: aload_1
149: ifnull 170
152: aload_0
153: invokevirtual #5 // Method close:()V
156: goto 174
159: astore 8
161: aload_1
162: aload 8
164: invokevirtual #7 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
167: goto 174
170: aload_0
171: invokevirtual #5 // Method close:()V
174: aload 7
176: athrow
177: return
Exception table:
from to target type
38 42 45 Class java/lang/Throwable
20 30 63 Class java/lang/Throwable
20 30 71 any
81 85 88 Class java/lang/Throwable
63 73 71 any
114 118 121 Class java/lang/Throwable
10 106 137 Class java/lang/Throwable
10 106 142 any
152 156 159 Class java/lang/Throwable
137 144 142 any
public static void f2() throws java.lang.Exception;
Code:
0: new #2 // class BogusResource
3: dup
4: invokespecial #3 // Method "<init>":()V
7: astore_0
8: aconst_null
9: astore_1
10: new #2 // class BogusResource
13: dup
14: invokespecial #3 // Method "<init>":()V
17: astore_2
18: aconst_null
19: astore_3
20: aload_0
21: invokevirtual #4 // Method read:()I
24: pop
25: aload_2
26: invokevirtual #4 // Method read:()I
29: pop
30: aload_2
31: ifnull 106
34: aload_3
35: ifnull 56
38: aload_2
39: invokevirtual #5 // Method close:()V
42: goto 106
45: astore 4
47: aload_3
48: aload 4
50: invokevirtual #7 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
53: goto 106
56: aload_2
57: invokevirtual #5 // Method close:()V
60: goto 106
63: astore 4
65: aload 4
67: astore_3
68: aload 4
70: athrow
71: astore 5
73: aload_2
74: ifnull 103
77: aload_3
78: ifnull 99
81: aload_2
82: invokevirtual #5 // Method close:()V
85: goto 103
88: astore 6
90: aload_3
91: aload 6
93: invokevirtual #7 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
96: goto 103
99: aload_2
100: invokevirtual #5 // Method close:()V
103: aload 5
105: athrow
106: aload_0
107: ifnull 177
110: aload_1
111: ifnull 130
114: aload_0
115: invokevirtual #5 // Method close:()V
118: goto 177
121: astore_2
122: aload_1
123: aload_2
124: invokevirtual #7 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
127: goto 177
130: aload_0
131: invokevirtual #5 // Method close:()V
134: goto 177
137: astore_2
138: aload_2
139: astore_1
140: aload_2
141: athrow
142: astore 7
144: aload_0
145: ifnull 174
148: aload_1
149: ifnull 170
152: aload_0
153: invokevirtual #5 // Method close:()V
156: goto 174
159: astore 8
161: aload_1
162: aload 8
164: invokevirtual #7 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
167: goto 174
170: aload_0
171: invokevirtual #5 // Method close:()V
174: aload 7
176: athrow
177: return
Exception table:
from to target type
38 42 45 Class java/lang/Exception
20 30 63 Class java/lang/Exception
20 30 71 any
81 85 88 Class java/lang/Exception
63 73 71 any
114 118 121 Class java/lang/Exception
10 106 137 Class java/lang/Exception
10 106 142 any
152 156 159 Class java/lang/Exception
137 144 142 any
}
*1:伝統的な命名規則。