オーバーライドを理解する

オーバーライドは継承したインスタンスメソッドを子クラスで再定義する仕組みです。同じ呼出しでも実体の型に応じた処理が選ばれます。引数が異なるオーバーロード、staticメソッドの隠蔽、フィールドの隠蔽とは区別します。

この単元で学ぶこと

  • オーバーライドの成立条件を判定する
  • 宣言型と実体型を分けて呼出し結果を求める
  • オーバーロード・static・finalの違いを判断する

本文解説

親の操作を子で変える ParentにString name(){return "P";}、Childに同じ宣言で"C"を返す実装を置くと、new Child().name()はCです。親の契約と呼出し方を保ちながら子の振舞いへ変えます。

親型からも子の実装 Parent p=new Child();p.name()もCです。コンパイルではParentにnameがあるかを調べ、実行時はChild実体のオーバーライドを選びます。参照変数の型だけで親本体を固定するわけではありません。

シグネチャと引数 親void set(int x)、子void set(int y)は引数名が違っても同じシグネチャです。子void set(long x)なら引数型が違うためオーバーロードで、親のint版を置き換えてはいません。

Override注釈 @Overrideを付けるとコンパイラに上書きの意図を検査させられます。名前を誤ってnam()と書いた場合、注釈付きならエラーです。正しく上書きしていれば注釈がなくても動的呼出しは働きます。

アクセス範囲 親publicメソッドを子でprivateやパッケージアクセスへ狭めることはできません。親型の利用者が呼べる契約を子も守る必要があるためです。逆に親のパッケージアクセスを同じパッケージの子でpublicへ広げることは可能です。

戻り値型 intを返す親メソッドを同じ引数でlongへ変えることはできません。参照型なら親の戻り値Objectを子でStringへ狭める共変戻り値は可能です。呼出し側が親の約束を満たす値として使えるかという観点で確認します。

finalとprivate finalメソッドは子で上書きできません。親privateメソッドは子へ継承されないため、子に同名メソッドを置いても親のprivateメソッドのオーバーライドではありません。見た目だけで同じ仕組みだと判断しないようにします。

staticとの違い 親子のstaticメソッドに同じ名前と引数を置く場合は隠蔽であり、参照の宣言型から選びます。親のインスタンスメソッドを子でstaticに変えることはできません。状態に応じた動的な振舞いを学ぶときは宣言のstatic有無を必ず見ます。

図解

宣言型で検査し実体で実行
引数が違うオーバーロードの選択は別段階で行われる。

重要ポイント

  • 同じ名前と引数型を持つ継承インスタンスメソッドを再定義する。
  • 親型参照でも実体のオーバーライドが選ばれる。
  • 公開範囲を狭くできず、戻り値型にも互換性が必要。
  • finalメソッドはオーバーライド不可、staticは動的オーバーライドではない。

間違えやすい点

  • 引数名の変更を別のメソッドだと考えない。
  • 引数型を変えた宣言をオーバーライドと思わない。
  • @Overrideが動的な実行先を作る魔法の指定だと考えない。

用語解説

オーバーライド
子が継承インスタンスメソッドを同じシグネチャで再定義すること。
オーバーロード
名前が同じで引数型や数が違う複数宣言を用意すること。
動的メソッド呼出し
実体の型に基づきオーバーライド先を実行する仕組み。

関連教材

参考資料

教材データに登録された参考リンクです。リンク先の内容や更新日は提供元でご確認ください。

確認問題の出題・採点や学習履歴の保存にはログインが必要です。