複数の catch ブロック
「エラーが出ました」だけでは、直しようがない
在庫数から「1 人あたり何個配れるか」を出す処理を考えます。ここで失敗する理由は 2 つあります。指定された商品番号が存在しないか、在庫が 0 で割り算ができないかです。
Java
try {
System.out.println(names[i] + " は 1 人あたり " + (100 / stock[i]) + " 個");
} catch (Exception e) {
System.out.println("エラーが出ました");
}これでも落ちはしません。ただ、画面に出るのは毎回「エラーが出ました」だけです。使う人は、番号を打ち直せばいいのか、在庫を補充すればいいのかが分かりません。例外を捕まえる目的は、落とさないことだけではなく、何が起きたのかを正しく区別して伝えることにあります。
catch は縦に並べられる
1 つの try に対して、catch は何個でも並べられます。投げられた例外の型に当てはまる最初の catch が実行されます。
Java
try {
System.out.println(names[i] + " は 1 人あたり " + (100 / stock[i]) + " 個");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("その番号の商品はありません");
} catch (ArithmeticException e) {
System.out.println("在庫が 0 なので計算できません");
}これで、番号が範囲外なら前者、在庫が 0 なら後者のメッセージが出ます。原因ごとに違う手当てができるようになりました。
並べる順番には決まりがあります。狭い型を先、広い型をあとに書きます。catch (Exception e) を先頭に置くと、その下の catch には二度と処理が届かないので、コンパイラが exception has already been caught と言って止めてくれます。
手当てが同じなのに、同じコードを 2 回書きたくない
原因が違っても、やることが同じ場合があります。「どちらの失敗でも、その行を飛ばしてログに残すだけ」といったケースです。このときは | で例外型を並べて 1 つの catch にまとめられます。
Java
try {
importRow(row);
} catch (ArrayIndexOutOfBoundsException | ArithmeticException e) {
System.out.println("この行は飛ばします " + e.getMessage());
}この書き方の中では、e は並べた型の共通の親として扱われます。ここでは RuntimeException なので、getMessage() のような共通のメソッドは呼べますが、どちらか一方にしかないメソッドは呼べません。
| で並べられるのは、親子関係にない型どうしだけです。catch (IOException | FileNotFoundException e) は、後者が前者の子なのでコンパイルエラーになります。
よくある間違い
「とりあえず広い網を張っておこう」と catch (Exception e) を先頭に書いてしまうのが定番のつまずきです。エラーメッセージだけ見ても意味が分かりにくいので、catch が並んでいるコードで急にビルドが通らなくなったら、まず順番を疑ってください。
もう 1 つ、戻り値や処理内容が違うのに | でまとめてしまうのも避けたいところです。まとめるのは手当てが同じときだけ、と決めておくと迷いません。
要件
- クラス名は
Solution、メソッド名はsafeDivide、引数はString a, String b、戻り値の型はint tryの中でInteger.parseIntを 2 回呼んでから割り算を行い、NumberFormatExceptionを捕まえるcatchで-1を返すこと- もう 1 つの
catchでArithmeticExceptionを捕まえて-2を返すこと。Exceptionでひと括りにせず、種類ごとに別々のcatchで書き分けること
入出力例
safeDivide("10", "2") → 5
safeDivide("10", "0") → -2
safeDivide("abc", "2") → -1
safeDivide("10", "xyz") → -1
safeDivide("-10", "2") → -5
safeDivide("7", "2") → 3