「Java でマサカリ投げてみた」の解説
JJUG CCC 2015 Fall の懇親会の LT で発表し、おかげさまで大好評をいただきました「Java でマサカリ投げてみた!」。
しかし、かなり早口で、しかも説明を端折っていたため、発表をご覧いただいた方に疑問を抱かせてしまいました。
- Masakari は本当に投げられたのか?
- UnhandledExceptionHandler が無ければ AbstractMethodError が発生する。つまり、本当は AbstractMethodError が投げられているのではないか?*1
そこで、この記事では Java で Masakari を投げたときに何が起きているのかを詳しく解説します!
すみません、、、先に断っておくとLTほど面白くないです… (−−;)
Masakari クラスは本当にスローされたのか?
Masakari クラスが本当にスローされたか、eclipse などのIDE(デバッガ)で確認できます。
ただ、IDEでいつも通りにデバッグしようとしてもコンパイルエラーになってしまいます。
そのため、スライドにある手順でコンパイル&実行して、それをIDEの "リモートデバッグ" という機能を使ってデバッグする必要があります。
詳しい手順は、GitHub の Masakari4j のページに記載していますので、こちらをご確認ください。
(ソースコード全文も、併せてコミットしています)
この手順で、"Unhandled Masakari" を出力している行でブレークポイントを張って実行してみると、このように arg1 (つまり、引数の Throwable e ) に Masakari オブジェクトが格納されているのが確認できます。
もちろん、 Throwable e = new Masakari(); というのは明らかに不正な代入です。
しかし、-noverify オプションで実行しているので、ノーチェックで代入が行われてしまうようです。
おそらく、本来であればこのような不正な処理はクラスロード時に Verifier がチェックしてエラーにしているので、実行時にはチェックしないのではないかと思います。*2
AbstractMethodError の原因
今回のプログラムは UnhandledExceptionHandler を入れて、独自にメッセージを出すようにしています。
しかし、これを削除してプログラムを実行*3すると、以下のエラーが発生します。
Exception in thread "main"
Exception: java.lang.AbstractMethodError thrown from the UncaughtExceptionHandler in thread "main"
この AbstractMethodError とはいったい何なのでしょうか。
これを先ほどと同じ手順でデバッグすると、以下の場所(ThreadGroup#uncaughtException 内で printStackTrace メソッドを呼び出したところ)で発生していることが分かりました*4。
つまり、これは以下の事象が起こっているということになります。
- Throwable クラスのつもりで printStackTrace メソッドを呼び出そうとした
- しかし、実際は Masakari クラスだったため、そのメソッドがない
- そのため、AbstractMethodError がスローされた
確認したところ、このような挙動について AbstractMethodError の Java Doc に記載がありました。
アプリケーションが抽象メソッドを呼び出そうとした場合にスローされます。通常、このエラーはコンパイラによって検出されます。現在実行中のメソッドを最後にコンパイルしたあとで、あるクラスの定義が変更されて互換性が失われた場合にだけ、実行時にこのエラーが発生します。
AbstractMethodError (Java Platform SE 8)
まとめ
ということで、Java でマサカリは本当に投げられる、というお話でした。
今思うと、"Masakari" ってハードコーディングするのではなく e.getClass().getSimpleName() としておけばよかったですね…。
もし、ほかに疑問点がありましたら、コメントいただければと思います。
あ、もちろんマサカリは受け付けていませんよ(><)