クラスファイルで見るマルチキャッチ(Multi-Catch)
JavaSE7で使えるMulti-Catch。
とっくの昔に調査済みだけど、
復習もかねてクラスファイル・エディターで見た内容を添えて書いてみます。
public void multiCatch() { try { multiThrow(); } catch (IOException | SQLException | ClassNotFoundException e) { e.printStackTrace(); } } public void multiThrow() throws IOException, SQLException, ClassNotFoundException { }
これをクラスファイル・エディターで見るとこうなります。(いらんところを削って)
public void multiCatch(); 0 aload_0 [this] 1 invokevirtual MultiCatch.multiThrow() : void [15] 4 goto 12 7 astore_1 [e] 8 aload_1 [e] 9 invokevirtual java.lang.Exception.printStackTrace() : void [18] 12 return Exception Table: [pc: 0, pc: 4] -> 7 when : java.io.IOException [pc: 0, pc: 4] -> 7 when : java.sql.SQLException [pc: 0, pc: 4] -> 7 when : java.lang.ClassNotFoundException
Multi-Catchで重要なのはException Tableです。
pc number 0-4の区間で例外が起こったばあい、
どのpc numberに飛ぶかがException Tableに書いてあります。
これ自体は昔からこういうものです。
見ての通り、どの例外が起きてもpc number 7に飛びます。
ここがこれまでと違うわけです。
では、JavaSE6の場合の例も添えてみましょう。
public void multiCatch() { try { multiThrow(); } catch (IOException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public void multiThrow() throws IOException, SQLException, ClassNotFoundException { }
とうぜんJavaSE6ではcatchが3つになります。
public void multiCatch(); 0 aload_0 [this] 1 invokevirtual MultiCatch.multiThrow() : void [15] 4 goto 28 7 astore_1 [e] 8 aload_1 [e] 9 invokevirtual java.io.IOException.printStackTrace() : void [18] 12 goto 28 15 astore_1 [e] 16 aload_1 [e] 17 invokevirtual java.sql.SQLException.printStackTrace() : void [23] 20 goto 28 23 astore_1 [e] 24 aload_1 [e] 25 invokevirtual java.lang.ClassNotFoundException.printStackTrace() : void [26] 28 return Exception Table: [pc: 0, pc: 4] -> 7 when : java.io.IOException [pc: 0, pc: 4] -> 15 when : java.sql.SQLException [pc: 0, pc: 4] -> 23 when : java.lang.ClassNotFoundException
catch句が分かれているため、Exception Tableで書かれている飛び先は全て違います。
catch句でやっている内容は全て違うコードとして扱われていますし、
JavaSE7の場合は型がExceptionと扱われている点にも注目しましょう。
違うコードと扱われているのは至極当然のことです。
コンパイラはスコープを意識していますから。
クラスファイルのサイズという点で言えば、
JavaSE7の方が効率的になっています。
今回は全て検査例外でやっていますが、
勝手にRuntimeException系をMulti-Catchに含めることも可能です。
その場合は、Exception Tableのエントリが1つ増えることになります。
私はcatchはショートカット(Ctrl+1)で補完するので、
"Surround with try/multi-catch"という候補がでます。
従来の"Surround with try/catch"も選べます。
さて、ちょっといじわるをして、
throws句をIOExceptionとFileNotFoundExceptionの2つにすると、
Multi-Catchはコンパイルエラーになります。
The exception {0} is already caught by the alternative {1}
JavaSE6だとコンパイル通るんですけどね。
これはJavaSE6では補完時に、
サブクラスのcatchを先に書いてくれてるので回避されてます。
仮にJavaSE6でIOExceptionを先にcatchするとコンパイルエラーになります。
Unreachable catch block for {0}. It is already handled by the catch block for {1}
JavaSE7のMulti-Catchの場合はサブクラスを先に書いてもコンパイルエラーです。
あと、100人が100人気持ち悪いと思うコード、
throws句にタイプパラメータを書けるんですが、
JavaSE6でもcatch句にタイプパラメータ使ってcatchすることは出来ません。
JavaSE7のマルチキャッチでも同様です。
そんなわけで、そんな気持ち悪いthrowsにタイプパラメータを書くなんてことは
当然やるんじゃないよって話ですかね。
結局のところ上限使ってキャッチすることになりますから。
じゃあ、なんで書けるんだ(笑