今回のテーマは、throw/throwsです。
throw/throwsは例外処理を勉強する時に登場する、掴みにくいキーワードだと思います。順番にしっかり把握して使い方をマスターしてしまいましょう。
※今回の記事は、実行時例外に対処するtry-catchの基本的な使い方を把握していることを前提にしています。
【サンプルコード】
まずはこちらのサンプルをご覧ください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Takeshi{ //たけし君の喉の調子を表現するフィールド boolean condition = false; //たけし君が歌うことを表現したメソッド //喉の調子が悪い場合は例外を発生させます void song()throws Exception{ if(condition){ System.out.println("素晴らしい歌"); }else{ System.out.println("ボエ~~~~"); throw new Exception(); //例外発生 } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
class Honekawa{ //骨川くんがリサイタルの運営をする様子を表現したメソッド public static void main(String[] args){ //たけし君をステージに呼びます Takeshi tk = new Takeshi(); //コマンドライン引数で声援をもらっていたら、喉の調子がUPします if(args.length > 0){ tk.condition = true; } //リサイタル開始です //喉の調子が良ければ、素晴らしい歌が聞けます try{ tk.song(); System.out.println("観客の拍手"); System.out.println("Happy End"); }catch(Exception e){ //喉の調子が悪かった場合、song()によって例外が発生しここに処理が遷移します System.out.println("→観客が次々倒れる"); System.out.println("責任を取って骨川君が救急車を呼ぶ"); System.out.println("Bad End"); } } } |
歌うことが好きなたけし君とその友人の骨川君がリサイタルを開いた様子を表現したプログラムです。こちらを例としながらthrow/throwsの解説をしていきます。
【例外を実際に投げるthrow】
throwはプログラマが任意のタイミングで実際に例外を投げたいときに使うキーワードです。
例えば配列などで存在しないインデックス番号を指定してしまった場合などは、実行時にJavaVMが明らかな問題点として、ArrayIndexOutOfBoundsExceptionを投げてくれます。
1 2 |
int array[] = {0,1,2,3,4}; System.out.print(array[5]); //ここで例外発生 |
しかし、今回のたけし君が歌うことを表現したsongメソッドが「喉の調子が悪かった(conditionがfalseであった)場合、例外にしたい」という考えで作られていた場合はどうでしょうか。JavaVMはそんな事情は分からず例外にしてほしいという記述が無い限り例外を投げてくれることはありません。
このような時はthrowというキーワードで「ここでは例外を投げます!」とプログラマが例外の発生を指示します。
1 2 3 4 5 6 7 8 |
void song()throws Exception{ if(condition){ System.out.println("素晴らしい歌"); }else{ System.out.println("ボエ~~~~"); throw new Exception(); //←ここで例外を発生させ投げる記述をしている } } |
throwは英単語そのまま「投げる」という意味です。
「ここで例外を投げたいんだよ!」という思いを込めて、余計な「s」などを付けず、throwと書きましょう。
そしてその後ろに発生させたい例外クラスのオブジェクトを書いてあげれば例外を発生させ投げることができます。
【throwsは例外発生を覚悟して使ってくださいねという予告】
例に挙げたsongメソッドは、throwsを使って定義していますが、try-catchをメソッド内に記述しても文法上構いません。
1 2 3 4 5 6 7 8 9 10 11 12 |
void song(){ if(condition){ System.out.println("素晴らしい歌"); }else{ System.out.println("ボエ~~~~"); try{ throw new Exception(); }catch(Exception e){ System.out.println("救急車を呼ぶ"); } } } |
しかしthrowsを使って定義をしておいた方が以下の点で便利です。
●例外が発生したことがきちんと伝わる
●例外への対処の柔軟性が上がる
上記のthrowsを使わないでメソッド内で例外に対処したメソッドは、songメソッド内で例外が起こり、tryで捕捉され、そのあとに記述されているcatch句で対処が完結しています。
そうするとmainメソッド(songメソッドの呼び出し側)には例外が発生したことは伝わらず、「救急車を呼ぶ」という出力処理をしたけれども、「観客の拍手」が発生するという矛盾した結果になってしまいます。
また、songメソッド内でtry-catchをしてしまうと、例外発生時の対処方法が必ずメソッド内に書いてある通りになってしまいます。
今回だと「救急車を呼ぶ」という対処方法に固定されてしまいますが、songメソッドが使われた状況に応じて別の対処(「猫型ロボットに助けてもらう」「たけし君の母親を呼んで叱ってもらう」など)をしたい場合もあるはずです。
別の対処方法に変えたい場合、その都度メソッドの定義を変更しなくてはならなくなり手間がかかります。
これらを解決するのが、throwsです。
上記の2つの問題点は、songメソッド内で例外への対処を完結させていることが原因です。
throwsを使うとメソッド内での例外への対処を免除させ呼び出し側にその対処を任せることが出来るようになります。
メソッドの定義にthrowsを記述することによって、「このメソッドは例外を投げるかもしれないけれど、予告はしてあるので、呼び出し側(今回はmainメソッド)でtry-catchなどの対策をしてよね」という意味になります。
例外を投げるかもしれない危険なメソッドと分かって使うのですから、サンプルのように呼び出し側でtry-catchなど何らかの対策をします。
これにより先に述べた、メソッド内で例外処理でどのように処理をするかを決めておく必要がなくなり、使う場面に応じた対応ができるようになります。
※複数の場所でsongメソッドが使われてもそれぞれの呼び出し側でcatch句の内容を調整することが出来ます。
1 2 3 4 5 6 7 8 9 10 11 |
try{ tk.song(); System.out.println("観客の拍手"); System.out.println("Happy End"); }catch(Exception e){ //別の処理をしたい場合、呼び出し側で調整可能! System.out.println("→観客が次々倒れる"); System.out.println("ネコ型ロボットを呼んでくる"); System.out.println("それっぽい道具で観客を助けてもらう"); System.out.println("Happy End?"); } |
【まとめ】
throwは実際に例外を投げたいときに使うキーワード、throwsはそのメソッドが例外を投げる可能性があることを予告する為のキーワードです。
ここまでで解説してきたそれぞれのキーワードの意味を踏まえて両者を使い分けられるように頑張ってみてください。
【おまけの余談】
「ちょっとここの内容(throw/throws)が難しいのでド●えもんで例えてもらえたりできますか?」
というリクエストをいただいたことがありまして、今回のサンプルは、その時、実際にお答えした内容を元にしたものとなっています。
何でもは、例えられないかもしれませんが、楽しく勉強できるように日々頑張っております。