オーバーライドとthrows

このレッスンでできるようになること。オーバーライドしたメソッドに書ける throws の範囲を判定し、コンパイルエラーになる形を見抜けるようになります。

まず予測してください

class Parent { void run() throws java.io.IOException { } } class Child extends Parent { @Override void run() throws Exception { } // これは通るでしょうか }

通りません。「オーバーライドされたメソッドより広い例外は投げられません」というコンパイルエラーになります。Exception は IOException の親、つまりより広いからです。

規則は「広げるな、狭めるのは自由」

親のメソッドが throws している検査例外に対して、子は次のことができます。

  1. 同じ例外を書く
  2. その例外のサブクラスを書く(狭める)
  3. throws を丸ごと消す(何も投げない)
  4. 検査例外を1つも書かない代わりに、非検査例外を好きなだけ投げる

できないのは1つだけで、親が申告していない検査例外や、親より広い検査例外を足すことです。理由は使う側から考えると分かります。Parent 型の変数に Child を入れて run を呼ぶ人は、Parent の throws しか知りません。そこに知らない検査例外が飛んでくると、catch の書きようがないからです。

非検査例外は対象外

RuntimeException とその子孫は、親が何も書いていなくても子が自由に投げられます。throws に書いても書かなくても構いません。これを「広げているからエラーだ」と誤答させるのが定番の引っ掛けです。この規則が縛るのは検査例外だけと覚えてください。

どの throws が効くのか

呼び出し側でどの例外を catch しないといけないかは、変数の型(コンパイル時の型)で決まります。実際に入っているオブジェクトが throws を消した子クラスでも、変数が親の型なら親の throws に合わせて catch が要ります。ここもポリモーフィズムと絡めて出ます。

確かめる

演習では、検査例外を throws する親のメソッドを、検査例外を投げない子でオーバーライドします。throws を消しても正しくオーバーライドできること、そして呼び出し側は変数の型に従って catch を書くことを、実行して確認してください。

要件

  1. ChildStore で load(String key) をオーバーライドする
  2. ChildStore の load には throws を付けない
  3. キーが空文字なら IllegalArgumentException を投げる
  4. そうでなければ "child-" + key を返す
  5. Store と MissingKeyException と call には手を加えない

入出力例

call("base", "a")"OK base-a" call("child", "a")"OK child-a" call("base", "")"MissingKeyException" call("child", "")"IllegalArgumentException"

ヒント

生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア
編集 LuaGate編集部
main.java
学習モード

メモ

オーバーライドとthrows

⌘S で保存