クラスの派生と継承
既存クラスのフィールドやメソッドといったメンバを有効的に利用するために、クラスの派生を行ってメンバを継承することができます。
宣言
クラスAから派生したクラスBを作成する場合は以下のように宣言します。
class B extends A { ... ... ... }
クラスAから見て、クラスBはサブクラス(下位)
クラスBから見て、クラスAはスーパークラス(上位)
注意事項
super(...)によって、スーパークラスのコンストラクタを呼び出せる
1 public class Test { 2 private int i=0; 3 4 Test(int i) { this.i = i; } 5 6 public int getI() { 7 return i; 8 } 9 }
1 public class ExtendTest extends Test { 2 private int j=0; 3 ExtendTest(int i) { super(i); } 4 ExtendTest(int i,int j) { super(i); this.j = j; } 5 6 public int getJ() { 7 return j; 8 } 9 }
1 public class TestTest { 2 public static void main(String[] args) { 3 ExtendTest et1 = new ExtendTest(100); 4 ExtendTest et2 = new ExtendTest(100,200); 5 6 System.out.println(et1.getI()); 7 System.out.println(et2.getI()); 8 System.out.println(et2.getJ()); 9 } 10 }
実行してみた結果
100 100 200
super(...)による、スーパークラスのコンストラクタの呼び出し部分。
ExtendTest(int i) { super(i); } ExtendTest(int i,int j) { super(i); this.j = j; }
なお、同一クラス内の他のコンストラクタを呼び出すthis(...)があるが、一つのコンストラクタ内でsuper(...)とthis(...)の両方を呼び出すことはできない。
ExtendTest(....) { super(...); this(...); } //エラー
スーパークラスのコンストラクタの自動呼び出し
また、スーパークラスのコンストラクタの呼び出しをしないサブクラスのコンストラクタ内では、スーパークラスの引数を受け取らないコンストラクタが自動的に呼び出される。言葉で説明するのは難しい。
例えば、サブクラスHogeで以下のようなコンストラクタを記述していたとする。
Hoge() {} Hoge(int a) {this.a = a}
これは以下のように書き換えられる。
Hoge() {super();} Hoge(int a) {super(); this.a = a; }
したがって、サブクラスではsuper(...)を明示的に呼び出すか、スーパークラス内で引数を受け取らないコンストラクタを定義する必要がある。
サブクラスでのインスタンス初期化子の実行タイミング
また、サブクラスHogeのクラス内でインスタンス初期化子を記述していた場合、super(...)が呼び出されてから、クラス初期化子が実行される。
Hoge() {super(); クラス初期化子の実行; } Hoge(int a) {super(); クラス初期化子の実行; this.a = a; }
サブクラスのデフォルトコンストラクタ
また、クラス内でコンストラクタを定義しないと以下のようにデフォルトコンストラクタが定義される。
Hoge() { }
クラスHogeがサブクラスだとすると、以下のようにsuper(...)の呼び出しが追加される。
Hoge() { super(); }
『super.メンバ名』による、スーパークラスメンバへのアクセス
1 public class Test { 2 protected int i=0; 3 4 Test() { } 5 Test(int i) { this.i = i; } 6 7 public void getI() { 8 System.out.println("I = "+i); 9 } 10 }
1 public class ExtendTest extends Test { 2 private int j=0; 3 ExtendTest(int i) { super.i = i; } 4 ExtendTest(int i,int j) { super.i = i; this.j = j; } 5 6 public void getJ() { 7 super.getI(); 8 System.out.println("J = "+j); 9 } 10 }
1 public class TestTest { 2 public static void main(String[] args) { 3 ExtendTest et2 = new ExtendTest(100,200); 4 et2.getJ(); 5 } 6 }
実行してみた結果
$ java TestTest I = 100 J = 200
ExtendTest(int i) { super.i = i; } ExtendTest(int i,int j) { super.i = i; this.j = j; }
ここでは、『super.i』によってスーパークラスのメンバであるインスタンス変数iにアクセスしています。protectedで宣言されているのでサブクラスからアクセス可能である。
public void getJ() { super.getI(); System.out.println("J = "+j); }
ここでは、『super.getI()』によってスーパークラスのメンバであるインスタンスメソッドgetIを呼び出している。
注意事項
ExtendTest(int i) { super.i = i; } ExtendTest(int i,int j) { super.i = i; this.j = j; }
ここの箇所はsuper(...)という記述がなく、スーパークラスのコンストラクタを明示的に呼び出していないので、以下のようにコンパイラによって定義されます。
>|java| ExtendTest(int i) { super(); super.i = i; } ExtendTest(int i,int j) { super(); super.i = i; this.j = j; }
よって、スーパークラスでは引数を取らないコンストラクタを定義しなければなりません。
以下の記述を忘れると、エラーになります。
Test() { }
すべてのクラスは、Objectクラスのサブクラス
すべてのクラスは、Objectクラスのサブクラスとなります。
今まで、extendsを付けずに宣言してきたクラスも、実はObjectクラスを継承していました。
class Hoge extends Object { ...
サブクラス型の変数はスーパクラスのインスタンスを参照できません
スーパークラス型の変数はスーパークラスとサブクラスのインスタンスを参照できますが、サブクラス型の変数はスーパークラスのインスタンスを参照できません。
例えば、スーパクラスTestとそのサブクラスExtendTestインスタンスが生成されている場合、
Test t = new Test(); ExtendTest et = new ExtendTest(); Test b; b =t; b = et; ExtendTest a; //a = t; //エラー a = et;
このようにサブクラス型の変数aがスーパークラスのインスタンスtを参照するとエラーになります。
すべてのクラスはObjectのサブクラスであるので、以下のように参照することもできます。
Object o; o = t; o = et;
派生において継承されるのは?
コンストラクタは継承されないことを学んだが、結局、なにが継承されて、なにが継承されないのか?