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:伝統的な命名規則。