きどたかのブログ

いつか誰かがこのブログからトラブルを解決しますように。

クラスファイルで見るマルチキャッチ(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にタイプパラメータを書くなんてことは

当然やるんじゃないよって話ですかね。

結局のところ上限使ってキャッチすることになりますから。

じゃあ、なんで書けるんだ(笑