2018年08月14日

師匠のJava道場~第12回・師匠、例外とはなんぞや!?

師匠T:ジャッ、ヴァッ!!

弟子D:ドゥッ! ジョウッ!!

師匠T:うむ、いい汗をかいたのう、弟子よ。ということで、師匠Tである。

弟子D:弟子Dだぜっ! 今回は俺がしゅや……ぐはぁっ(師匠の飛び蹴り炸裂

師匠T:おぬしが主役など、2365年早いと何度も言っておるであろうが。さて、今回は、例外について説明する。準備はよいか?

弟子D:は、はい、師匠……

▽まず、例外とは?▽

弟子D:いきなりですが師匠。例外とはなんぞや。

師匠T:うむ。例外とは、いわゆるエラーのようなものじゃ。

弟子D:エラーのようなもの……ですか。

師匠T:うむ。Java……に限らず、他の言語でもそうだが、わしらが良く使う広義のエラーの概念は、昔の言語とは大きく違っておるのだ。

弟子D:それは一体?

師匠T:Javaでは、いわゆるエラーは二つに分かれておる。それが、例外とエラーじゃ。

弟子D:例外とエラー……ですか。それは、どう違うのですか?

師匠T:エラーとは、Javaでは手に負えない異常な出来事をさす。対して、例外とはJavaで扱える異常なことをさすのじゃ。

弟子D:ふむふむ。

師匠T:そして、この例外も二つにわかれておる。ランタイム系例外と、非ランタイム系例外じゃ。

弟子D:ランタイム系と、非ランタイム系……。ランタイムと、そうでないもの、という感じですか?

師匠T:そんな感じじゃな。具体的に言うと、ランタイム系は、コンパイルによってチェックされないもの、非ランタイム系は逆に、コンパイルによってチェックされる例外なのじゃ。

弟子D:なるほど。ちょっとややこしいですね。

師匠T:そんなときには、『いわゆる異常には、エラーと例外の二つがある』と覚えておけばよい。これでも、さほど問題はないからの。

弟子D:はい!

▽エラーの扱い方は……ない!▽

師匠T:エラーが出たときの対処法だが……

弟子D:ごくり。

師匠T:そんなものは、ない!!

弟子D:えええええええ!?

師匠T:エラーを、後述するtry~catchでとらえることはできる。じゃが、とらえたところでどうしようもないから、エラーが起きたら仕方ないと諦めるのが定例となっておる。

弟子D:そ、そうなのですか……。

▽例外を捕らえるtry~catchじゃ!▽

師匠T:そして例外の対処法じゃが……。

弟子D:まさか、そんなものはない、とかいうつもりじゃ……。

師匠T:安心するがよい。Javaには例外の発生が、プログラムの強制終了につながらないようにするための仕組みがある。それが、try~catch構文じゃ。

弟子D:try~catchですか。

師匠T:うむ。これは、発生した例外を捕らえ、そのときに処理を分岐させる構文なのじゃ。このようにして使う。

----------
try {
例外が発生するかもしれない処理
} catch (〇〇〇 e) {
〇〇〇例外が発生した時の処理
}
----------

師匠T:この構文では、まずtryとcatchの間の処理を行う。そして、〇〇〇に指定された例外が発生したときに、catchの下の処理を行うようになっておるのじゃ。

弟子D:なるほど。あの師匠、一つ質問が。

師匠T:なんじゃ?

弟子D:複数の種類の例外を捕らえたいときはどうするのですか?

師匠T:そのときには、catchの下に、さらに続けてcatchを書くのじゃ。このようにの。

----------
try {
〇〇〇

} catch (aaaException e) {
△△△

} catch (bbbException e) {
□□□

}
----------

弟子D:aaaExceptionが発生したら△△△の処理が、bbbExceptionが発生したら□□□の処理が実行されるわけですね?

師匠T:うむ。また、finallyというのもある。これは、例外が発生した、しないに関わらず、必ずその処理が終わったあと必ず実行する処理を書くものじゃ。このように使う。

----------
try {
〇〇〇

} catch (aaaException e) {
△△△

} catch (bbbException e) {
□□□

} finally {
×××
}
----------

師匠T:この例だと、例外が起こらなかった場合は、〇〇〇の処理を実行した後に×××の処理が実行され、例えばbbbExceptionが発生した後は、□□□の処理を実行した後に×××の処理が実行されるのじゃ。

弟子D:なるほど。

師匠T:なお、一度例外が発生して、catchの下に処理が移った場合、元のtryの下の処理に戻す方法はないので注意するのだ。

弟子D:はい!

▽例外のクラス構造じゃ!▽

師匠T:さて、ではここで例外のクラス構造について説明しておくとしようぞ。さて、例外のクラスの大本はExceptionだが、これのスーパークラスはThrowableクラスという。さらにこのThrowableの大本、スーパークラスは、わかるかな?

弟子D:むむむ……。

師匠T:第9回の講義を思い返すがよい。Throwableのスーパークラスは、全てのクラスの大本じゃ。それは何であった?

弟子D:あ! Objectクラスですね!

師匠T:うむ、そのとおりじゃ。さて、Throwableから派生したサブクラスとして、例外を表すExceptionクラスと、Errorクラスの二つがある。

弟子D:ふむふむ。

師匠T:そして、このExceptionクラスのサブクラスに、ランタイム系例外を表すRuntimeExceptionクラスがあるのじゃ。

弟子D:なるほど。あれ? 師匠、非ランタイム系例外は? ははーん、もうボケ……いてっ。

師匠T:たわけが。わしはまだ若い。非ランタイム系例外もちゃんと、Exceptionクラスのサブクラスとして存在しておる。

弟子D:な、なるほど……。

師匠T:このクラス構造は、この先のことについて重要となる。頭の片隅にでも入れておくがよい。

▽eとはなんぞや?▽

師匠T:さて、次はこの部分に注目じゃ。

----------
catch (aaaException e)
----------

師匠T:このeは、aaaExceptionで指定したExceptionクラスのインスタンスじゃ。いわば、発生した例外についての情報が入ったインスタンスなのじゃ。

弟子D:ふむふむ。

師匠T:なお、別に名前はeである必要はない。e1でもabcでもOKじゃ。さて、このインスタンスにはprintStackTraceメソッドが備え付けられておる。これを実行することで、例外が発生したメソッドに至るまでの、実行したメソッドの順番を表示することが可能じゃ。デバッグに役立つであろう。

弟子D:そうですね。覚えておくとします。

師匠T:うむ。

▽スーパークラス側の例外を補足じゃ!▽

師匠T:さて。

----------
catch (aaaException e)
----------

師匠T:この部分の、aaaExceptionのところには、普通はそれぞれの例外の型を書くものじゃが、その例外のスーパークラスを書くことも可能じゃ。例えば、ランタイム系例外を補足するときに、その例外の型ではなく、RuntimeExceptionクラスを指定する、という感じじゃな。

弟子D:ふむふむ。そうすることで、どんなメソッド……じゃなかったメリットがあるのでしょうか?

師匠T:うむ。これを利用することで、複数の例外を一つのcatchで捕らえ、処理することが可能じゃ。

弟子D:おぉっ!!

師匠T:ただし、その例外がどんな例外なのかがわかりにくくなるので、そこは気を付ける必要があるぞ。

弟子D:使い分けが大事、ということですね。わかりました!

▽throwsじゃ!▽

師匠T:さて、クラスを宣言する部分じゃが、これにはthrowsという言葉をつけることができる。

弟子D:それはどのようなもので?

師匠T:うむ。これは、『このメソッドで、これこれの例外が発生する可能性がある』が、『このメソッドでは、その例外の処理はしない』というのを表すものじゃ。これを利用する場合は、その例外は、別のメソッド(たいていは発生したメソッドの呼び出し元)で処理することになる。このような感じじゃな。

----------
void 〇〇〇() {
try {
△△△();
} catch (xxxException e) {
□□□

}
}

void △△△() throws xxxException {
×××

}
----------

弟子D:なるほど。

▽例外を投げるのじゃ!▽

師匠T:さて。Javaでは、発生した例外を捕らえ、処理するだけではない。オリジナルの例外を作り、発生させることができるのじゃ。なお、これを『例外を投げる』ともいう。

弟子D:なるほど。そのプログラム独自の例外を発生するのに使えそうですね。どのようにするのですか?

師匠T:うむ。それには、throwを使うのじゃ。このように使う。

----------
throw new [例外のクラス]

例:throw new MalformedURLException
----------

師匠T:例のように、既存の例外を投げることもできるぞ。他方、オリジナルの例外を作る場合は……
・ランタイム系例外を投げる→RuntimeExceptionクラスを継承するクラスを作り、投げる
・非ランタイム系例外を投げる→Exceptionクラス、またはRuntimeException以外の、Exceptionのサブクラスを継承するクラスを作り、それを投げる
のじゃ。例としては、このような感じじゃ。

----------
public class SisyoException extends RuntimeException {

}

public class Sisyo1 {
private static void abc() {
throw new SisyoException();
}
}
----------

弟子D:なるほど!

師匠T:ランタイム系例外を投げる場合、特段、コードを記述しなくても処理することができるが、プログラムが強制終了するリスクを負う。非ランタイム系を投げた場合は、そのようなリスクはないが、処理するためのコードを書く必要がある。
throwsで、そこでは処理しないことを明示するか、try~catchを書くか、ということじゃな。どっちを利用したほうがいいか、ケースバイケースなのでよく考えることが必要じゃ。

弟子D:はい、考えません、師匠!

師匠T:……(無言で弟子に飛び蹴り)さて、次回はいよいよ最終回。インターフェイスについて説明する。予習、復習はしっかりの。



※次の更新は、8月16日、『師匠TのチャレンジARSゲーム!』の予定です。お楽しみに!
posted by 裏編 at 09:26| 師匠のJava道場 | 更新情報をチェックする