オブジェクト指向②継承

オブジェクト指向の記事は→こちら

オーバーライドの記事は→こちら

Javaでは、すでに定義してあるクラスを拡張して、新しいクラスを定義することができます。

そして、あるクラスのメンバを受け継いで別のクラスを作ることもできます。

このことを継承と言います。 

継承のもととなるクラスをスーパークラス

それを拡張して新しく作成したクラスをサブクラスと呼びます。

継承では基本的にextendsというキーワードを使います。このキーワードを書くことで継承先の機能、属性を継承し、自分のモノと使えるというものです。これを入れるだけで継承先の内容をすべて手に入れられるという事です。現在あるクラスに差分(追加)で機能を実装したいときなどに使えるという感じでしょうか。

サブクラスをインスタンス化すると、スーパークラスで定義した変数やメソッドはすべてサブクラスに引き継がれます。

修正する際も、サブクラス含めてすべての継承クラスを修正する必要はなく、スーパークラスの一部を修正するだけで、サブクラスは修正後の内容を継承するのでメンテナンスも簡単ですね。

※サブクラスが継承で引き継ぐのはスーパークラスで定義した変数とメソッドです。コンストラクタとprivateなフィールドとメソッドは引き継がれないので注意!!!

コンストラクタはそのクラスの固有のメソッドなのでそのクラスは渡さない!ということです。

なぜかというと、継承の際にそのクラスのコンストラクタに依頼をかけるのでそれぞれコンストラクタはそのクラス自身で最低限持っているということです。

実際に継承においてコンストラクタを呼び出す際はsuper()というキーワードを使います。後述します。

  • 哺乳類クラス ⇐ 犬クラス ⇐ 犬種クラス ⇐ ペットクラス(飼っている犬)
  • 国クラス ⇐ 県クラス ⇐ 市クラス ⇐ 町クラス ⇐ 番地クラス ⇐ 家クラス
  • 人間クラス ⇐ 性別クラス ⇐ 年齢クラス ⇐ 個人クラス

大分類や、PCのエクスプローラやJavaのファイルのイメージが近いですかね。大きな属性・機能を引き継いでいき、サブクラスでどんどん補完していき、作る人(mainメソッド)で呼び出し、実行し完成品を作るイメージですね。

サブクラスが親クラスを持つ(コンストラクタ以外)と思ってもいいです。

サブクラスを定義する際の構文

  • [修飾子]class サブクラス名 extends スーパークラス名{ }

普通のクラスの宣言にextendsと継承したい先のクラス名を追加する感じですね。

※キーワードの後に指定できるクラスは1つのみなので注意!

継承パターンを紹介します。A,B,Cのクラスがあると定義します。

  • A(スーパー) ← B(サブ) = OK
  • A(スーパー) ← B(サブ) & C(サブ) = OK
  • A(スーパー) ← B(サブ) ← C(サブ) = OK
  • A(スーパー) & B(スーパー) ← C(サブ) = NG

1つのサブクラスで一気に2つのクラスを継承するのはできない。それ以外はOK という風に覚えていいかもですね。一気に2つのスーパークラスを持つことができない特性を「単一継承」といい単一継承のみが認められている。というワードがよく出てきます。

NGの例は逆に「多重継承」と言われます。

またインスタンス化した際に関してはサブクラスがスーパークラスの内容を持っているのでメソッドを呼び出す際はスーパークラスのメソッドだとしても、サブクラスのインスタンスで呼び出すことができます。例えば、インスタンス変数名をcとしてスーパークラス名が A、メソッドを spica とします。  

C c = new C();  //サブクラスをインスタンス化

  • c.spica();

CクラスにはspicaメソッドはないですがAクラスにあるspicaメソッドをcクラスのインスタンスで呼び出すことができます。フィールド変数も同じようにメソッドと同じように呼び出すことができます。

継承関係を持つクラスのメンバを呼び出す際はサブクラス⇒1つ先のスーパークラス⇒もう一つ先のスーパークラスという順に呼び出していきます。

サブクラスで見つからなければ上記の順番で探しに行くというイメージです。

継承において、1つ先のスーパークラスのコンストラクタを呼び出す際に使うキーワードです。

  • super();

これで呼び出します。this();と似ていますね。

また、スーパークラスの持つメソッドやフィールドもsuper();で呼び出せます。構文は以下

  • super.メソッド名();
  • super.フィールド名();

これで呼び出せます。

this()は自身のインスタンスが抱えているコンストラクタの 

super();はスーパークラスが抱えているコンストラクタ

という違いになります。

1つ先のスーパークラスのコンストラクタを呼び出し、1つ先のスーパークラスでsuper();を使うとそのまた1つ先のスーパークラスのコンストラクタを呼び出すことができます。

またコンストラクタの記事で、this();で呼び出した場合、呼び出した先のコンストラクタが先に動く(処理される)

その代わりthis();は必ずコンストラクタの一番上に記載するというルールがあったと思います。これが破られるとコンパイルエラーになります。

super();も同様に、コンストラクタの最初に記載する必要があります。そして一番先のsuper();から先に動作(処理)されます。

流れとしては 

  1. mainメソッドでサブクラスをインスタンス化
  2. サブクラスの最初でsuper();を実行し1つ先のスーパーを呼び出し。
  3. 1つ先のスーパークラスの最初にsuper();を実行し大元のスーパークラスを呼び出し。
  4. 大元のスーパークラスから順に処理開始
  5. サブクラスに戻る
  6. mainメソッドのインスタンスオブジェクトへ戻る

となります。

コンストラクタの動きをまとめると

  • コンストラクタは継承されない
  • インスタンス化の際にフィールドの初期値をせっていするのはそのクラスのコンストラクタであり、スーパークラスのフィールドを初期化したい場合はそのコンストラクタを呼び出す必要がある。
  • super(引数);を使う事でサブクラス内で明示的に1つ先のスーパークラスのコンストラクタを呼び出すことができる。
  • 継承関係(extends)を持つクラスをインスタンス化する際、コンストラクタはかならず上位クラス(深いクラス)から順に呼び出す必要がある。その為、スーパークラスを呼び出す際は必ずsuper();をコンストラクタの最初に記載する必要がる。
  • 最初に記載する必要があるのでsuper();やthis();は2つ以上使用できない点に注意!!!
  • super();やthis();は併用もできない!!(エラーになる)
  • サブクラスのコンストラクタでsuper();を記述しなかった際は暗黙的にsuper();が実行される。

コメント

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