みなさまこんにちは。こんばんは。
Numata です。
今回は継承クラスのコンストラクタについて解説します。
コンストラクタの基本についてはこちらにまとめてあるのでご参照ください。
今回は、こちらの記事の④のルールである、
継承しているクラスの場合、親クラスのコンストラクタが必ず処理される
について解説します。
コンストラクタは、継承が混じると本当に厄介です。
継承クラスのインスタンスの仕組み・コンストラクタのルール
の2点が大切になります!
それではいってみましょう!
もくじ
子クラスのコンストラクタ内で、親クラスのコンストラクタが実行されます。
コンストラクタはインスタンス化されるときに、
1番最初に実行されるメソッドだということはご理解いただけていると思います。
問題は、インスタンス化したいクラスが継承(extends)しているクラスだったときです。
親クラスにも、子クラスにもコンストラクタが設定されていた場合、
どのような動きになるか想像はつくでしょうか。
・コンストラクタが上書きされて子クラスのコンストラクタのみ実行される
・親クラスのコンストラクタは無視される
・親クラスでコンストラクタが設定されているからコンパイルエラーになる etc…
上記のような予想の答えは全部ノーです。
確かに、コンストラクタはインスタンス化の時に実行されるメソッドというルールに
従うと、親クラスのインスタンス化の式が書かれていない場合、実行されなそうです。
しかし、継承クラスをインスタンス化するときは、
子クラスのコンストラクタ内で、親クラスのコンストラクタが実行される
のです。
これもルールの一つです。
継承クラスのインスタンス化の際、
子クラスのコンストラクタ内で必ず super(); というメソッドが実行されます。
明記されていないときは自動作成されます。
デフォルトコンストラクタと似ていますね。
この super(); というメソッドは、
親クラスのコンストラクタを実行する
という働きがあります。
つまり、強制的に親クラスのコンストラクタが実行されるというルールがある
ということです。
サンプルプログラムで確認してみましょう
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Hero { String name; int hp; int power; int defense; public Hero() { this.name = "ヒーロー"; } public void attack() { System.out.println(this.name + "の攻撃だ!攻撃力は" + this.power + "だ!"); } } |
1 2 3 4 5 6 7 8 9 10 11 |
public class SuperHero extends Hero{ //コンストラクタなし int specialPower; //attackメソッドのオーバーライド public void attack() { System.out.println(this.name + "の攻撃だ!攻撃力は" + (this.power+this.specialPower) + "だ!"); } } |
親クラス:Hero
子クラス:SuperHero
という関係になっています。
Heroクラスでは、コンストラクタが設定されています。
この状態でSuperHeroクラスをインスタンス化すると、
SuperHeroクラスでは、コンストラクタが自動作成されます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class SuperHero extends Hero{ //コンストラクタなし //デフォルトコンストラクタ(自動作成) SuperHero(){ super(); } int specialPower; //attackメソッドのオーバーライド public void attack() { System.out.println(this.name + "の攻撃だ!攻撃力は" + (this.power+this.specialPower) + "だ!"); } } |
super(); は、子クラスのコンストラクタの中の1番最初に自動作成されます。
そして()の中には引数を渡すことができます。
つまり!
もし親クラスのコンストラクタに引数が必要だった場合は、
子クラスで明示的に super(); を書かなければなりません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class Hero { String name; int hp; int power; int defense; //引数ありのコンストラクタしかない場合 public Hero(String name) { this.name = name; } public void attack() { System.out.println(this.name + "の攻撃だ!攻撃力は" + this.power + "だ!"); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class SuperHero extends Hero{ //コンストラクタを明示して、文字列を渡さなければならない SuperHero(){ super("スーパーヒーロー"); } int specialPower; //attackメソッドのオーバーライド public void attack() { System.out.println(this.name + "の攻撃だ!攻撃力は" + (this.power+this.specialPower) + "だ!"); } } |
継承クラスは、親クラスのコンストラクタを確認しましょう。
親クラスに、引数必須のコンストラクタしかなかった場合、
子クラスのコンストラクタ内で super(引数); を明記する必要があります!
継承クラスのインスタンスは、内部に親クラスのインスタンスを持っています
なぜ super(); が自動作成されるのか。
その答えは、
インスタンスの内部にインスタンスを持っているからです。
これはコンストラクタとは少し離れるのですが、大事なところです。
継承クラスのインスタンスは、複数のインスタンスで構成されています。
以下の流れで作成されます。
[継承クラスのインスタンス作成手順]
①親クラスのインスタンスがつくられる
②子クラスにしかないメンバがインスタンスとしてつくられる(差分インスタンス)
③子クラスの内部に親クラスを持って、インスタンスとして完成する
図で見るとこんな感じです。
外見はSuperHeroインスタンスですが、内部にHeroインスタンスを持っています。
これが継承クラスのインスタンスの仕組みです。
図は球体になっていることをイメージしてください。
一見外側から見るとSuperHeroインスタンスにした見えませんが、
実は内部に親クラスあるHeroインスタンスを持っている ことがわかると思います。
継承クラスのインスタンスはひとつのインスタンスからできているわけではない
ということを押さえましょう!
コンストラクタはインスタンス化されるときに必ず実行されます
ここで改めて思い出していただきたいことが、コンストラクタのルールです。
インスタンス化されるときに必ず最初に実行される
というルールがありましたよね。
このルールを守るために super(); が存在します。
子クラスのインスタンスも内部に親クラスのインスタンスを持っています。
ということは、親クラスのインスタンスができているということです。
インスタンスができるときは必ずコンストラクタが実行される必要があるため、
super();で実行しているのです。
super(); は、子クラスのコンストラクタの一番上で明示(もしくは自動生成)
というルールと合わせると、継承クラスのインスタンス作成手順①~③になるのです。
コンストラクタはルールです。
インスタンス化するときに必ず実行されなくてはいけません。
継承クラスは複数インスタンスから形成されます。
内部に親クラスのインスタンスを保持しています。
この二つを組み合わせると、
子クラスのインスタンスのコンストラクタの1番上で super(); が実行される
という決まりが見えます!
継承クラスでは、親クラスのコンストラクタを実行しましょう。
継承クラスのインスタンス化は注意すべきことがたくさんありました。
ここでもう一点ルールです。
super(); は、一つ前のインスタンスにしかアクセスできません。
もし何重にも継承しているクラスがあった場合、
おおもとの親クラス以外には、全て super(); がついています。
コンストラクタは覚えるべきルールがたくさんあります。
一つずつ抑えて、マスターしましょう!
ご覧いただきありがとうございました。
コメント