オーバーロードとオーバーライド

今回はオーバーロードとオーバーライドについてまとめていきます。呼び出しなどオブジェクト指向の中で出てくるものなので、それぞれ決まりがあったり名前が似ていたりとこれも覚える必要があるなと感じるので残そうと思います。

オーバーロードと関連のある内容はこちら ⇒カプセル化インスタンスコンストラクタ

オーバーライドと関連のある内容はこちら ⇒継承

試験対策としてはどっちがどのオブジェクト指向の要素なのか、仕組みの違いなどを覚える必要があるので上記記事と一緒にイメージするようにしましょう。

Java言語では一つのクラス内に、同じ名前のメソッドやコンストラクタを複数定義することができるみたいです。

このことをオーバーロードと言います。

なぜ複数定義する必要があるか。メソッドの呼び出しに関係しています。コンストラクタやインスタンスで別のクラスを使うためにメソッドの呼び出しを行ったと思います。その際に、複数メソッドがあり特定のメソッドを呼び出す際には呼び出し側とメソッド定義側で、引数の型を合わせる必要があります。例えば

呼び出したいメソッドが Testクラスの void spica(); であれば 

呼び出す側は  Test t = new Test(); t.spica();

呼び出したいメソッドが Testクラスの void spica(int item); であれば 

呼び出す側は  Test t = new Test(); t.spica(100);

といったふうに呼び出したいメソッドの引数に合わせて呼び出す必要があります。

たとえ処理が同じであっても、引数の型や数が異なる場合、それぞれメソッドを別で定義する必要があるわけです。

ただ、目的は同じで引数だけ違うのにメソッド名を変えて分けて定義をし続けるのには管理も使い分けも大変になります。それに対応するために、同じ名前のメソッドやコンストラクタを定義できる「オーバーロード」という仕組みがあるわけです。

定義にはルールがあります。

  • 引数の並び
  • 引数の型
  • 引数の数

がそれぞれ異なっていることです。

オーバーロードはコンストラクタとメソッドに適用できます。上記ルールはコンストラクタもメソッドも同じです。

※戻り値(voidなど)、アクセス修飾子(public、privateなど)は上記の条件に含まれません。なので同じでも異なっていてもいいということです。

※引数の名前だけ変えてもオーバーロードとはみなされないので注意。

オーバーロードは仕組みみたいなものなので、宣言みたいな構文という構文はないのですが例を出します

  • void spica( ) { }  //引数無し
  • void spica(int a) { }  //int型の引数1つ
  • void spica(int a, int b) { }  //int型の引数2つ
  • void spica (String c) { }  //String型の引数1つ(型違い)
  • void spica (String d, int e) { }  //String型の引数1つ、int型の引数1つ
  • void spica (int f, String g) { }  //引数の順番(並び)が違う

これらすべて1つのクラスに同時に定義できます。spicaメソッドを上で紹介したルールにのっとって定義してあり、これがspicaメソッドがオーバーロードされた状態になっているということです。

コンストラクタにも適用ができるのでSpicaクラスがあるとするとSpica(int a) { }のようにspicaというメソッド名を全部クラス名に変えてコンストラクタの例として置き換えることも可能です。

右にあるコメントを見て比較してみると全部引数が微妙に違っているのでわかりやすいですよね。

継承関係があり、目的が同じで処理が異なるメソッドをサブクラスに定義する場合、スーパークラスで定義されたメソッド名と全く同じ名前で再定義することができます。

このことをオーバーライドといいます。

例えば、Aクラスに「計算する」メソッドが定義されています。AクラスをBクラスで継承します。BクラスではAクラスの「計算する」メソッドをっ引き継ぎます。しかしBクラスでは「計算する」メソッドの処理内容を変更したいと考えました。

処理内容が異なると、スーパークラス(Aクラス)のメソッドをそのまま使えないので、Bクラスなどで新たにメソッドを定義する必要があります。ただ、そうするとメソッド名を変更する必要があり、Aクラスで「計算する」メソッド、Bクラスで「計算する2」メソッドという風になり、呼び出すときもそれぞれ「計算する()」と「計算する2()」という風に考えて呼び出さなければならなくなります。

それを簡単にしたのがオーバーライドというわけですね。継承関係にあるクラスであれば、再定義ができるというイメージでしょうか。

サブクラス側で処理を変えたいという場合に使います。

また、サブクラスではスーパークラスで定義したメンバ変数と同じ名前を持つ、メンバ変数も宣言が可能です。

オーバーライドについてのルールは以下の通りです。

  • メソッド名、引数リストが全く同じメソッドをサブクラスで定義する。
  • 戻り値はスーパークラスで定義したメソッドが返す型と同じか、そのクラスのサブクラス型とする。
  • アクセス修飾子は、スーパークラスと同じものか、それよりも公開範囲が広いものであれば使用可能である。

引数と戻り値が特に注意するポイントです。

引数の数、並び、型はスーパークラスと全く同じ必要があります。

戻り値はスーパークラスで定義したメソッドがじゃえす方と同じか、その方のサブクラス型です。(voidであればvoid、StringであればString、A型であればA型/ただしA型をスーパークラスに持つB型がある場合、オーバーライドしたメソッドはB型でも指定可能。)

  • public void spica(String s){System.out.println(“引数は” + s)}  //親クラス
  • public void spica(String s){System.out.println(“オーバーライドして” + s + “に書換”)}  //子クラス

こういう感じですね。例えばルールにあるアクセス修飾子を呼び出す側で厳しくすると

  • public void spica(String s){}  //親クラス 修飾子はpublic
  • private void spica(String s){}  //子クラス 修飾子はprivate

これはコンパイルエラーになります。

呼び出す側(子クラス)のオーバーライドしたメソッドのアクセス修飾子は親クラスのオーバーライドするメソッドのアクセス修飾子よりも同じか優しくある必要があります。

  • オーバーロードはカプセル化の一種。引数さえ変えれば同じ名前のメソッドとコンストラクタを定義できる仕組み
  • オーバーライドは継承の一種。継承先のメソッドの処理だけ変えたい場合に引数リストとメソッド名を全く同じ、戻り値も全く同じか、サブクラス型

という感じですね。オーバーライドは継承限定の仕組みな感じがするのでセットで覚えるのがよさそうですね。

コメント

タイトルとURLをコピーしました