支援対象地域:札幌、仙台、関東、愛知、関西、広島、福岡

  • TOP
  •   
  • コラム
  •   
  • Java オブジェクト指向2(継承)

はじめに

Javaのオブジェクト指向を実現する上で重要となる3つの機能(カプセル化、継承、多態性)について3回にわたってご紹介します。 今回ご紹介する機能は、継承です。

継承(1)

1)類似したクラスの作成

以前作成したクラスとほとんど同じクラス(メソッドを一つだけ追加する等)を作る場合、「最初に作成したクラスをコピー&ペーストして作成する」がまず思い浮かと思います。 しかし、コピー&ペーストではいくつかの問題点があります。

《コピー&ペーストの問題点》
1.追加や修正に手間がかかる
2.管理が難しくなる    

Animalクラスをコピー&ペーストして、talkメソッドを追加したHumanクラスを作成した例で考えてみます。

1.追加や修正に手間がかかる

Animalクラスに新しいメソッドを追加した場合や、Animalクラス内のメソッドを変更した場合は、その変更をHumanクラスにも行う必要があります
その理由として、HumanはAnimalの一部であり、AnimalができることはHumanもできなければならないためです。

2.管理が難しくなる

AnimalクラスとHumanクラスの違いは、talkメソッドの有無のみで、それ以外のコードは重複することになります。
プログラム全体の見通しが悪くなり、管理が難しくなります。 また、これは、保守性を低下させる要因になります。

コピー&ペーストで類似したクラスを作成していくことは、現実的とは言えません。
これらの問題点を解決して、類似したクラスを作成する機能こそ「継承」ということです。
継承を用いたクラスの定義は、extends修飾子を用いて次のように行います。

 アクセス修飾子 class クラス名 extends 元となるクラス名{
  元となるクラスとの「差分」メンバを記述
 }

つまり、Humanクラスは次のように書き換えられるということです。

//Animalクラス
public class Animal{
 //フィールド
 String name;
 int year;

 //メソッド
 public void run(){…}
 public void eat(){…}
 public void sleep(){…}
}
//Humanクラス
public class Human extends Animal{
//Animalクラスとの「差分」メンバ
 //メソッド
 public void talk(){…}
}

Humanクラスでは、talkメソッドのみ記述していますが、Animalクラスのメンバを利用できます。
しかし、Animalクラスでは、talkメソッドは利用できません。

Animalクラスのように、継承の元となるクラスを「親クラス」、「スーパークラス」と呼びます。
また、Humanクラスのように、親クラスを継承して新たに定義されるクラスを「子クラス」、「サブクラス」と呼びます。

継承関係を図で表現する場合は、イメージ図のように「サブ(子)クラス」から「スーパー(親)クラス」に向かって矢印を記述するルールになっています。



2)継承の注意点

継承を行う上で、気を付けなければならないことがあるので、ご紹介します。

  《継承の注意点》

  • スーパークラス内のprivate修飾子が付いたメンバは、サブクラスから利用不可
  • final修飾子が付いたスーパークラスは、継承不可


  • 継承は、「is-a」の関係で行う
  • サブクラスとスーパークラスの関係が、「サブクラス is a スーパークラス」となるように継承を行ってください。
    「is-a」の関係が不成立ならば、ラクができるとしても継承を使わないようにしましょう。


  • 1つのサブクラスが継承できるスーパークラスは、1つのみ
  • 1つのサブクラスに対して2つ以上のスーパークラスを継承することはできません。
    一方で、1つのスーパークラスに対して複数のサブクラスを持つことは可能です。



継承(2)

1)オーバーライド

サブクラスでスーパークラスのメソッドの動きを変えたい場合は、サブクラスでスーパークラスのメソッドを書き換えます。 このことをオーバーライドといいます。
似た言葉にオーバーロードがありますが、全く異なるものになります。

  • オーバーライド
  • 継承時に、スーパークラスのメンバをサブクラス側で上書きすること。
  • オーバーロード
  • 同じ名前のメソッドを複数定義すること。

スーパークラスの全てのメソッドをオーバーライドできるわけではありません。 オーバーライドするにはいくつかの条件があります。
オーバーライドの条件として、次の条件があります。

  《オーバーライドの条件》

  • final修飾子が付いていない
  • メソッド宣言にfinal修飾子が付くとオーバーライド不可になります。

  • オーバーライドするメソッドにはstaticは付けられない
  • スーパークラスで非staticメソッドの場合、サブクラスでstaticメソッドとしてオーバーライドすることはできません。

  • スーパークラスと同じメソッド名である

  • スーパークラスと同じ引数(データ型、個数)

  • スーパークラスと同じ戻り値である

  • スーパークラスと同じアクセス修飾子 or スーパークラスより制限が緩いアクセス修飾子である
  • 【アクセス修飾子】
    制限 名前 プログラム内の
    アクセス修飾子
    アクセスの許可範囲
    厳しい



    緩い
    private private 自分自身のクラスのみ
    package private (何も記述しない) 自分と同じパッケージ内のクラス
    protected protected 自分と同じパッケージ内 or 自分を継承したクラス
    public public 全クラス

2)スーパークラスの利用

super」を利用することで、サブクラスでオーバーライドしていても、スーパークラスのメソッドを使用することができます。

  • スーパークラスのフィールドを利用する
  • super.フィールド名;

  • スーパークラスのメソッドを呼び出す
  • super.メソッド名(引数);

  • スーパークラスのコンストラクタを呼び出す
  • 「super();」を書かなくても、「暗黙のsuper();」が自動挿入されます。
    super(引数);
    ※ただし、サブクラスのコンストラクタの先頭行にしか記述できません
//Animalクラス
public class Animal{
 //メソッド
 public void sleep(){ System.out.println("Animalクラスのsleepメソッド"); }

 //コンストラクタ
 public class Animal(){ System.out.println("Animalクラスのコンストラクタ"); }
}
//Humanクラス
public class Human extends Animal{

 //メソッド
 //sleepメソッドをオーバーライド
 public void sleep(){
  System.out.println("Humanクラスのsleepメソッド");
  super.sleep();  //スーパークラスのsleepメソッド呼び出し
 }

 //コンストラクタ
 public class Human(){
  super();  //スーパークラスのコンストラクタ呼び出し
  System.out.println("Humanクラスのコンストラクタ");
  //先頭行以外に「super();」を記述するとエラーになる
 }
}
//Mainクラス
public class Main {
 public static void main(String[] args) {
  Human h = new Human();
  h.sleep();
 }
}
実行結果
Animalクラスのコンストラクタ
Humanクラスのコンストラクタ
Humanクラスのsleepメソッド
Animalクラスのsleepメソッド

MainクラスでHumanクラスをインスタンス化したことで、Humanクラスのコンストラクタが呼び出されます。 しかし、Humanクラスのコンストラクタでは、スーパークラスであるAnimalクラスのコンストラクタを呼び出しているため、実行結果は、
Animalクラスのコンストラクタ
Humanクラスのコンストラクタ

となります。

また、sleepメソッドでは、Humanクラスでオーバーライドした後にAnimalクラスのsleepを呼び出しているため、実行結果は、
Humanクラスのsleepメソッド
Animalクラスのsleepメソッド

となります。


まとめ

継承」についてご紹介しました。 お役に立てると幸いです。