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修飾子を用いて次のように行います。
元となるクラスとの「差分」メンバを記述
}
つまり、Humanクラスは次のように書き換えられるということです。
public class Animal{
//フィールド
String name;
int year;
//メソッド
public void run(){…}
public void eat(){…}
public void sleep(){…}
}
public class Human extends Animal{
//Animalクラスとの「差分」メンバ
//メソッド
public void talk(){…}
}
Humanクラスでは、talkメソッドのみ記述していますが、Animalクラスのメンバを利用できます。
しかし、Animalクラスでは、talkメソッドは利用できません。
Animalクラスのように、継承の元となるクラスを「親クラス」、「スーパークラス」と呼びます。
また、Humanクラスのように、親クラスを継承して新たに定義されるクラスを「子クラス」、「サブクラス」と呼びます。
継承関係を図で表現する場合は、イメージ図のように「サブ(子)クラス」から「スーパー(親)クラス」に向かって矢印を記述するルールになっています。
2)継承の注意点
継承を行う上で、気を付けなければならないことがあるので、ご紹介します。
《継承の注意点》
- スーパークラス内のprivate修飾子が付いたメンバは、サブクラスから利用不可
- final修飾子が付いたスーパークラスは、継承不可
- 継承は、「is-a」の関係で行う サブクラスとスーパークラスの関係が、「サブクラス is a スーパークラス」となるように継承を行ってください。
- 1つのサブクラスが継承できるスーパークラスは、1つのみ 1つのサブクラスに対して2つ以上のスーパークラスを継承することはできません。
「is-a」の関係が不成立ならば、ラクができるとしても継承を使わないようにしましょう。
一方で、1つのスーパークラスに対して複数のサブクラスを持つことは可能です。
継承(2)
1)オーバーライド
サブクラスでスーパークラスのメソッドの動きを変えたい場合は、サブクラスでスーパークラスのメソッドを書き換えます。
このことをオーバーライドといいます。
似た言葉にオーバーロードがありますが、全く異なるものになります。
- オーバーライド 継承時に、スーパークラスのメンバをサブクラス側で上書きすること。
- オーバーロード 同じ名前のメソッドを複数定義すること。
スーパークラスの全てのメソッドをオーバーライドできるわけではありません。
オーバーライドするにはいくつかの条件があります。
オーバーライドの条件として、次の条件があります。
《オーバーライドの条件》
- final修飾子が付いていない メソッド宣言にfinal修飾子が付くとオーバーライド不可になります。
- オーバーライドするメソッドにはstaticは付けられない スーパークラスで非staticメソッドの場合、サブクラスでstaticメソッドとしてオーバーライドすることはできません。
- スーパークラスと同じメソッド名である
- スーパークラスと同じ引数(データ型、個数)
- スーパークラスと同じ戻り値である
- スーパークラスと同じアクセス修飾子 or スーパークラスより制限が緩いアクセス修飾子である
制限 | 名前 | プログラム内の アクセス修飾子 |
アクセスの許可範囲 |
厳しい
⇔ 緩い |
private | private | 自分自身のクラスのみ |
package private | (何も記述しない) | 自分と同じパッケージ内のクラス | |
protected | protected | 自分と同じパッケージ内 or 自分を継承したクラス | |
public | public | 全クラス |
2)スーパークラスの利用
「super」を利用することで、サブクラスでオーバーライドしていても、スーパークラスのメソッドを使用することができます。
- スーパークラスのフィールドを利用する
- スーパークラスのメソッドを呼び出す
- スーパークラスのコンストラクタを呼び出す 「super();」を書かなくても、「暗黙のsuper();」が自動挿入されます。
※ただし、サブクラスのコンストラクタの先頭行にしか記述できません
public class Animal{
//メソッド
public void sleep(){ System.out.println("Animalクラスのsleepメソッド"); }
//コンストラクタ
public class Animal(){ System.out.println("Animalクラスのコンストラクタ"); }
}
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();」を記述するとエラーになる
}
}
public class Main {
public static void main(String[] args) {
Human h = new Human();
h.sleep();
}
}
Humanクラスのコンストラクタ
Humanクラスのsleepメソッド
Animalクラスのsleepメソッド
MainクラスでHumanクラスをインスタンス化したことで、Humanクラスのコンストラクタが呼び出されます。
しかし、Humanクラスのコンストラクタでは、スーパークラスであるAnimalクラスのコンストラクタを呼び出しているため、実行結果は、
Animalクラスのコンストラクタ
Humanクラスのコンストラクタ
となります。
また、sleepメソッドでは、Humanクラスでオーバーライドした後にAnimalクラスのsleepを呼び出しているため、実行結果は、
Humanクラスのsleepメソッド
Animalクラスのsleepメソッド
となります。
まとめ
「継承」についてご紹介しました。 お役に立てると幸いです。