オーバーライドについて
1.4 オーバーライドについて
これまでの学習ではサブクラスを作成する際に、新規機能(メソッド)を追加するように拡張してきました。そこでサブクラスに新しくメソッドを追加する時には、スーパークラスと全く同じ定義(メソッド名、引数の数、型、並び)のメソッドを追加することができます。その機能をオーバーライド(override)と言います。
オーバーライドとは
スーパークラス(親クラス)で定義されたインスタンスメソッドを、そのクラスを継承したサブクラス(子クラス)で独自に定義し直して上書きすることをいいます。サブクラスで機能を追加する必要がある場合などにオーバーライドを利用します。なお、オーバーライドを利用するには、メソッド名、戻り値の型、引数の型と数及び並びが完全に同じでなければなりません。
オーバーライドのメリット
・スーパークラスから継承したメソッドの代わりに、サブクラスのメソッドを動作させることができる。
・1つのメソッド名でそのオブジェクトのクラスに応じて、適切な処理(メソッド)を行わせることができる。
・複数のメソッド名を定義したり覚えたりする必要がなくなる。
上記で示したメリットではあまり便利な機能に感じないかもしれませんが、オーバーライドを行うのは「継承したスーパークラスのメソッドでは実現したい機能が満たせない、でも利用するメソッド名を変更することができない」といった場合になります。別の名前でメソッドを作成すればいいではないかと思うかもしれませんが、別の名前で似たような処理のメソッドを乱立させると管理が大変になる理由があり、このオーバーライド機能が提供されている理由にもなっています。
コラム・ オーバーロード(Overload)
同じクラス内で、メソッド名が同じで引数の形式が異なるメソッドを定義すること。
・ オーバーライド(Override)
スーパークラスのメソッドとメソッド名・戻り値の型・引数の形式が完全に同じであるメソッドをサブクラス内で再定義すること。
ではスーパークラスとサブクラスに同じ定義のメソッドがある場合、どちらのメソッドが呼び出されるのかをソースコードから確認していきます。
1.4.1 スーパークラスと同じ定義のメソッドをサブクラスに用意するプログラム
スーパークラスと同じ定義のメソッドをサブクラスに用意して、どちらのメソッドが動作するのか確認します。メソッドのオーバーライドについて学習しましょう。
ソースコード
➢ Computer2.java ※スーパークラス1.3.7で作成したクラスを利用します。
➢ NotePc5.java ※サブクラス① ソース・フォルダー :myproj_basic/src
② パッケージ :jp.co.f1.basic.ch11
③ 名前 :NotePc5
package jp.co.f1.basic.ch11; //Computer2クラスを継承 public class NotePc5 extends Computer2 { private String useType; //用途タイプ //コンストラクタ(引数なし) public NotePc5() { this.useType = null; System.out.println("ノートパソコンを作成しました。"); } //コンストラクタ(引数あり) public NotePc5(String os , int memory , String useType){ //スーパークラスのコンストラクタを呼び出す super(os,memory); this.useType = useType; System.out.println("タイプは" + this.useType + "用のノートパソコンを作成しました。"); } public void setUseType(String useType){ this.useType = useType; System.out.println("タイプは" + this.useType + "用にしました。"); } //スーパークラスと同じメソッドを定義 public void show(){ System.out.println("このノートパソコンのOSは" + super.os + "です。"); System.out.println("メモリサイズは" + super.memory + "です。"); System.out.println("タイプは" + this.useType + "用です。"); } }
➢ OverrideMethod.java ※mainメソッドを持つ実行クラス① ソース・フォルダー :myproj_basic/src
② パッケージ :jp.co.f1.basic.ch11
③ 名前 :OverrideMethod
④ 作成するメソッド・スタブの選択:public static void main(String[] args) にチェックを入れる
package jp.co.f1.basic.ch11; public class OverrideMethod { public static void main(String[] args) { //サブクラスのオブジェクトを生成 NotePc5 npc = new NotePc5("Windows7",3072,"ビジネス"); npc.show(); } }
実行結果
解説
6行目でNotePc5クラスのオブジェクト生成を引数ありのコンストラクタで行っています。引数にはOS名、メモリ数、用途の3つの情報を渡しています。
7行目でshow()メソッドを呼び出しています。このshow()メソッドはスーパークラスにもサブクラスにも定義されているメソッドになっています。
実行結果を見ると直ぐに分かりますが、サブクラスのshow()メソッド処理内容のメッセージ出力が行われているのが確認できます。
先程のプログラムのスーパークラスとサブクラスのメソッド内容を比較してみましょう。
図 1.4.1: メソッドのオーバーライド
処理内容を見比べるとスーパークラスは2行メッセージを出力、サブクラスは3行メッセージを出力するようになっています。実行結果は3行のメッセージが出力されていたので、間違いなくサブクラスのshow()メソッドが呼び出されているのが分かります。
図 1.4.2: メソッドのオーバーライドで呼び出されるメソッドについて
ポイント
- スーパークラスと同じ定義のメソッドを持つサブクラスから、そのメソッドを呼び出すとサブクラスのメソッドが呼び出される。
1.4.2 スーパークラスとサブクラスでsuperとthisを同時に使用するプログラム
サブクラスのオーバーライドしたメソッドから、スーパークラスのメソッドを呼び出せることを確認します。
ソースコード
➢ Computer2.java ※スーパークラス1.3.7で作成したクラスを利用します。
➢ NotePc6.java ※サブクラス① ソース・フォルダー :myproj_basic/src
② パッケージ :jp.co.f1.basic.ch11
③ 名前 :NotePc6
package jp.co.f1.basic.ch11; //Computer2クラスを継承 public class NotePc6 extends Computer2 { private String useType; //用途タイプ //コンストラクタ(引数なし) public NotePc6() { this.useType = null; System.out.println("ノートパソコンを作成しました。"); } //コンストラクタ(引数あり) public NotePc6(String os , int memory , String useType){ //スーパークラスのコンストラクタを呼び出す super(os,memory); this.useType = useType; System.out.println("タイプは" + this.useType + "用のノートパソコンを作成しました。"); } public void setUseType(String useType){ this.useType = useType; System.out.println("タイプは" + this.useType + "用にしました。"); } //スーパークラスと同じメソッドを定義 public void show(){ super.show(); System.out.println("タイプは" + this.useType + "用です。"); } }
解説
このNotePc6クラスはNotePc5クラスの内容から、show()メソッドの処理内容のみが変更になっているクラスです。その変更部分について説明します。
25行目でスーパークラスのshow()メソッドを呼び出しています。「super」キーワードをつけることで、サブクラス内のオーバーライドしたメソッド内からでも、スーパークラスのメソッドが呼び出すことができます。
今回のプログラムと違ってオーバーライドされたメソッドが無ければ「super」キーワードは省略可能です。
➢ UseSuperClassMethod.java ※mainメソッドを持つ実行クラス① ソース・フォルダー :myproj_basic/src
② パッケージ :jp.co.f1.basic.ch11
③ 名前 :UseSuperClassMethod
④ 作成するメソッド・スタブの選択:public static void main(String[] args) にチェックを入れる
package jp.co.f1.basic.ch11; public class UseSuperClassMethod { public static void main(String[] args) { //サブクラスのオブジェクトを生成 NotePc6 npc = new NotePc6(); //スーパークラスのメンバを呼び出す npc.setOsMemory("Windows7", 3072); //サブクラスのメンバを呼び出す npc.setUseType("モバイル"); //オーバーライドされているメソッドを呼び出す npc.show(); } }
実行結果
解説
6行目でNotePc6クラスのオブジェクトを生成しています。扱う変数も同じNotePc6クラスにしています。
9行目でスーパークラスのsetOsMemory()メソッドを利用してOSとmemory情報の設定を行っています。
12行目ではサブクラスのsetUseType()メソッドを利用して用途タイプ情報の設定を行っています。
15行目でオーバーライドしたshow()メソッドを呼び出して、各情報を画面に表示しています。このオーバーライドしたサブクラスのshow()メソッドから、スーパークラスのshow()メソッドが呼び出されています。
実行結果からも確認できるように、スーパークラスのshow()メソッドとサブクラスのshow()メソッドでの処理が正しく行われています。
ポイント
- オーバーライドしたサブクラスのメソッド内から、スーパークラスのメソッドを呼び出すことができる。その際はsuperキーワードを使う必要がある。
繰り返し説明になってしまいますが、今回のようにオーバーライドしたメソッド内からsuperキーワードを利用すればスーパークラスの同じメソッドも利用することができます。部分的な処理をつけ加えるだけのオーバーライドを行いたい場合には、スーパークラスの処理をそのまま利用すればサブクラス内の処理記述を楽に行えるようになります。既にある処理を再利用するという考え方は、プログラムの基本ですので実践できるようにしましょう。
コラムサブクラス内ではスーパークラスと同一名のメンバを使うケースも出てきます。
メソッドの場合はオーバーライドされ呼び出しの優先が変わりますが、フィールド変数にはオーバーライド機能はない為、同一名でもクラス毎の値を持つことになります。
スーパークラスのメンバは「super」、サブクラスのメンバは「this」をつけてアクセスすれば区別することが可能になります。
クラスの拡張とfinal修飾子の関係性について
1.4節では、オーバーライド機能について学習してきました。しかし、クラスによってはオーバーライドされては困るメソッドがあるかもしれません。このような場合のクラスにはfinal修飾子を利用すればオーバーライドを禁止することができます。さらにクラス拡張自体させたくない場合も同様に禁止することができます。
final修飾子について
finalは上書きされないことを意味します。使う場面に応じて様々な処理を禁止することができます。
final修飾子の特徴
1)メソッドにfinalをつけると、サブクラスからのオーバーライドを禁止できる。
2)クラスにfinalをつけると、クラスの継承を禁止できる。
3)フィールド変数にfinalをつけると、値の変更を禁止できる。
finalを用いた基本構文を以下に示します。
凡例を見ても分かるように、オーバーライドとクラス拡張を禁止するのはfinalを戻り値の型とclassの前に記述すればいいだけですので大変簡単です。クラスの設計に応じてクラスの拡張や、メソッドのオーバーライドを制御しましょう。
次項では、クラス、メソッド、フィールド変数にfinalをつけてコンパイルエラーになることを、実際にのエラーが発生している画像を使って確認していきます。
1.4.3 final修飾子を利用して機能を制御する
スーパークラスにfinal修飾子を用いて、サブクラスからオーバーライドやクラスの拡張ができないことを確認します。
・オーバーライドを禁止する
・クラスの拡張を禁止する
ポイント
- メソッドにfinalをつけると、サブクラスからのオーバーライドを禁止できる。
- フィールドにfinalをつけると定数となり、値の変更を禁止できる。
- クラスにfinalをつけると、クラスの継承を禁止できる。