継承について2回にわたって解説しました。
今回は最終回です。
継承時のコンストラクタ
前回のプログラムを再度見てみましょう。
public class Human{ private String name; private int age; private double height; public Human( ){ System.out.println("引数なしコンストラクタが呼ばれました!"); } public Human(String n, int a, double h){ this( ); System.out.println("引数ありコンストラクタが呼ばれました!"); name = n; age = a; height = h; } public void setName(String n){ name = n; } public void setAge(int a){ age = a; } public void setHeight(double h){ height = h; } public void selfIntroduce( ){ System.out.println("私の名前は" + name + "です"); System.out.println("私の年齢は" + age + "です"); System.out.println("私の身長は" + height + "です"); } }
public class Salaryman extends Human{ private int salary; private int bonus; public Salaryman( ){ System.out.println("Salarymanクラスの引数なしコンストラクタが呼ばれました!"); } public Salaryman(String n, int a, double h, int s, int b){ System.out.println("Salarymanクラスの引数ありコンストラクタが呼ばれました!"); setName(n); setAge(a); setHeight(h); salary = s; bonus = b; } public int getIncome( ){ return salary * 12 + bonus * 2; } }
public class UseHuman{ public static void main(String[ ] args){ Salaryman taro = new Salaryman("太郎", 30, 175.5, 30, 60); //Humanクラスのメソッド taro.selfIntroduce( ); //Salarymanクラスのメソッド int income = taro.getIncome( ); System.out.println("収入は" + income + "です"); } }
main( )メソッドからSalarymanクラスのインスタンスを生成していますが、コンストラクタはSalarymanの引数ありのものしか呼ばれていません。
しかし、実行結果を見ると引数なしのものがまず実行され、それから引数ありのものが実行されています。
これはいったいどういうことでしょうか。
Salarymanクラスの引数なしコンストラクタが呼ばれているのでしょうか?
実は、ここで実行されているコンストラクタはHumanクラスのコンストラクタなのです。
クラスが継承をしているとき、インスタンスを生成するとスーパークラスのコンストラクタも呼び出されます。
この場合はSalarymanクラスの親がHumanクラスで、さらにObjectクラスがその親です。
すべてのクラスのコンストラクタが自動的に呼び出され、順番もObjectクラスからと決まっています。
thisキーワードを使用すれば同一クラスのコンストラクタを呼び出せますが、それをしなければコンストラクタは各クラス1つずつ呼び出されます。
ポイント!スーパークラスのコンストラクタの呼び出し
あるクラスのインスタンスを生成すると、そのクラスのスーパークラスのコンストラクタも呼び出されます。
しかし、呼び出されるのは引数なしのコンストラクタと決まっています。
もし(例えばHumanクラスの)引数ありコンストラクタを呼び出したいときにはどうしたら良いでしょうか?
それには「super」というキーワードを使います。このキーワードは引数によって、どのコンストラクタを呼び出すかを指定することができます。
【superの利用】
「Salaryman」クラスを下記のように編集します。
public class Salaryman extends Human{ private int salary; private int bonus; public Salaryman( ){ System.out.println("Salarymanの引数なしコンストラクタが呼ばれました!"); } public Salaryman(String n, int a, double h, int s, int b){ super(n, a, h); System.out.println("Salarymanの引数ありコンストラクタが呼ばれました!"); /* setName(n); setAge(a); setHeight(h); */ salary = s; bonus = b; } public int getIncome( ){ return salary * 12 + bonus * 2; } }
「Human」クラスを下記のように編集し、どのコンストラクタが呼び出されたかわかりやすいようにします。
public class Human { private String name; private int age; private double height; public Human( ){ System.out.println("Humanの引数なしコンストラクタが呼ばれました!"); } public Human(String n, int a, double h){ this( ); System.out.println("Humanの引数ありコンストラクタが呼ばれました!"); name = n; age = a; height = h; } public void setName(String n){ name = n; } public void setAge(int a){ age = a; } public void setHeight(double h){ height = h; } public void selfIntroduce( ){ System.out.println("私の名前は" + name + "です"); System.out.println("私の年齢は" + age + "です"); System.out.println("私の身長は" + height + "です"); } }
実行結果
Humanの引数なしコンストラクタが呼ばれました! Humanの引数ありコンストラクタが呼ばれました! Salarymanの引数ありコンストラクタが呼ばれました! 私の名前は太郎です 私の年齢は30です 私の身長は175.5です 収入は480です
Humanのコンストラクタが二つ呼び出されていますが、これはHumanの引数ありコンストラクタではthisキーワードによって引数なしコンストラクタも明示的に呼び出しているからです。
superキーワードによって、スーパークラスのコンストラクタを選択することができます。
superを使わなくても引数を取らないコンストラクタが呼ばれるので、これは呼び出すというよりも選択するという感じです。
ポイント!superの記述位置
スーパークラスのコンストラクタを選択するsuperキーワードは、呼び出し側のコンストラクタで一番上に書く必要があります。
これを守らないとコンパイルエラーになります。
これは、SalarymanよりもHumanのコンストラクタが先に実行されるので、Salarymanのコンストラクタを実行する前に呼び出さないといけないのです。
まとめ
継承
- extendsキーワードを使うことによってクラスの機能を継承できます。
- サブクラスではスーパークラスの機能を利用することができます。
- サブクラスでも、スーパークラスのprivateメンバにはアクセスできません。その場合protectedを用います。
- JavaのすべてのクラスはObjectクラスを継承しています。
- 多重継承は認められていません。
クラス間の関係
- 継承はis-a関係です。
- クラスのフィールドに他のクラスのオブジェクトを持つのはhas-a関係です。
- メソッド内で一時的に他のクラスを使用するのはuseです。
継承時のコンストラクタ
- スーパークラスのコンストラクタはsuperで指定できます。
- super呼び出しはコンストラクタの先頭になければなりません。
- 継承関係にあるクラス間で、サブクラスのインスタンスが作成されると、暗黙のsuper()が挿入され、サブクラスのコンストラクタの実行前に、スーパークラスの引数なしコンストラクタが自動的に呼ばれます。
- 明示的にスーパークラスのコンストラクタの呼び出しを記述すると、暗黙のsuper()は挿入されません。
独学で挫折しそうになったら、オンラインプログラミングスクール