検査例外と非検査例外
このレッスンでできるようになること。throws を書かないとコンパイルが通らない例外と、書かなくても通る例外を、その場で見分けられるようになります。
まず予測してください
次の2つのメソッドのうち、コンパイルできるのはどちらでしょうか。
static int a(String s) {
return Integer.parseInt(s); // NumberFormatException が飛ぶ可能性がある
}
static int b(String s) {
throw new java.io.IOException("だめ"); // IOException が飛ぶ
}答えは a だけです。b は「例外 IOException は報告されません」というコンパイルエラーになります。どちらも例外を投げうるのに、片方だけ怒られます。この差が Silver の頻出ポイントです。
境目は RuntimeException と Error
Java の例外はすべて Throwable の子孫です。その下は大きく3つに分かれます。
- Error とその子孫(OutOfMemoryError など)は非検査例外です
- RuntimeException とその子孫(NullPointerException、NumberFormatException、IllegalArgumentException、ArrayIndexOutOfBoundsException など)は非検査例外です
- それ以外の Exception の子孫(IOException、SQLException、そして自作の「Exception を継承した例外」)は検査例外です
検査例外は「投げる可能性があるなら、その場で catch するか、メソッドに throws と書いて呼び出し元に押しつけるか、どちらかをやりなさい」とコンパイラが強制します。非検査例外にはこの強制がありません。
引っ掛けの作り方
出題者は次の形を好みます。
- 自作例外に
extends Exceptionと書いて、呼び出し側で何もしない。検査例外なのでコンパイルエラーです - 自作例外に
extends RuntimeExceptionと書いて、throws が無いのを「エラーだ」と思わせる。こちらは正しく通ります throwsが付いたメソッドを try で囲んでいるが、catch している型が検査例外の親でもなんでもない。やはりコンパイルエラーです
覚え方は「名前に Runtime か Error が入る血筋なら自由、それ以外の Exception の血筋なら申告が要る」です。
確かめる
演習では、検査例外と非検査例外の両方が飛びうるメソッドを1つの try で受け止め、飛んできた例外のクラス名を返します。catch (Exception e) は検査例外も非検査例外もまとめて捕らえられます。実行して、同じ書き方で両方が捕まることを目で確認してください。
要件
- parse(s) の呼び出しを try で囲み、catch (Exception e) で受け止める
- 成功したときは "OK " + 値 を返す
- 例外が飛んだときは e.getClass().getSimpleName() を返す
- parse と EmptyInputException には手を加えない
入出力例
check("12") → "OK 12"
check("") → "EmptyInputException"
check("abc") → "NumberFormatException"ヒント
編集 LuaGate編集部