catchの順序
このレッスンでできるようになること。catch を並べる順番だけでコンパイルエラーになる形と、マルチキャッチが使えない形を、見た瞬間に判定できるようになります。
まず予測してください
try {
Integer.parseInt("abc");
} catch (Exception e) {
System.out.println("広い");
} catch (NumberFormatException e) {
System.out.println("狭い");
}これは実行されません。コンパイルエラーです。2つめの catch には絶対に到達できないからです。1つめの Exception が NumberFormatException も含めて全部さらってしまうので、後ろの catch は死んだコードになります。Java はこれを「例外 NumberFormatException はすでに捕捉されています」として拒否します。
規則は2つだけ
- catch は上から順に「この型に代入できるか」で判定されます。最初に当てはまった1つだけが実行されます
- 上の catch の型が下の catch の型のスーパークラスなら、下は到達不能なのでコンパイルエラーです
したがって catch は狭い型から広い型へ並べます。逆は書けません。並びが兄弟同士(継承関係が無い型同士)なら、どちらが先でも構いません。ここも狙われます。IllegalArgumentException と IllegalStateException は兄弟なので順序は自由です。
マルチキャッチの落とし穴
複数の型をまとめて1つの catch で受けられます。
catch (IllegalArgumentException | IllegalStateException e) { }これは通ります。しかし次はコンパイルエラーです。
catch (IllegalArgumentException | NumberFormatException e) { }NumberFormatException は IllegalArgumentException のサブクラスです。マルチキャッチに継承関係のある型を並べることはできません。片方が余分だからです。もうひとつ、マルチキャッチの変数 e は暗黙に final なので、catch の中で e に別の例外を代入し直すこともできません。
覚えておきたい階層
NumberFormatException は IllegalArgumentException の子で、IllegalArgumentException と IllegalStateException は RuntimeException の子です。この3段を頭に入れておくと、順序問題はほぼ落としません。
確かめる
演習では、この3つを投げ分けるメソッドを狭い順に受け止めます。マルチキャッチも1つ使います。並べ替えてわざとエラーにしてみると、コンパイラの言い分が読めて記憶に残ります。
要件
- catch は NumberFormatException を先に書く
- IllegalArgumentException と IllegalStateException は1つのマルチキャッチで受ける
- NumberFormatException のときは "NFE" を返す
- マルチキャッチでは e.getClass().getSimpleName() を返す
- 例外が出なければ "OK " + 値 を返す
入出力例
pick(1) → "NFE"
pick(2) → "IllegalArgumentException"
pick(3) → "IllegalStateException"
pick(4) → "OK 40"