前回はカプセル化について学習しました。
カプセル化はオブジェクト指向を理解する上で非常に重要な要素で、つながっているデータと操作を同じ場所で管理することで修正箇所を限定することと、内部情報を隠ぺいしてオブジェクト利用者に不要な情報を渡さず、シンプルに利用できるようにする意義を覚えていれば大丈夫です。
今回はこれまたオブジェクト指向を理解する上で重要な「継承」の概念や実装方法について学習していきます。継承は実際の現実にもある概念なので、比較的分かりやすいかと思います。
なお、以下の実行環境を前提にしています。
OS:Windows 10 Pro
IDE:Eclipse 4.8 Photon(Pleiades)
Java:Java8
目次
1.継承
継承とは、カプセル化と並んでオブジェクト指向の概念を構成する要素の1つです。
(1)継承とは
具体的な例の前に、継承についての概念的な解説を入れたいと思います。
継承とは、あるオブジェクト「A」が他のオブジェクト「B」の属性や操作などの特徴を一部保有している場合のことを指します。または、オブジェクト「B」がオブジェクト「A」の特性を引き継いでいるとも言えます。こういった継承関係を、よく「A is a B(AはBの一種である)」という言い方をします。なので、AとBは意味的に同じようなクラス間にのみ継承関係が成り立ちます。
少し固い話になってしまいましたが、言っていることは至極当たり前のことです。具体的な例を見てみましょう。
(2)継承の具体的な実装例
継承についてはこのページでも取り上げています。
継承、および汎化(はんか)と特化について、具体例を挙げています。かいつまんで話すと、汎化は特性を抽象化することで、特化は具体化することです。ここではHumanクラスを使って具体例な実装例を挙げてみます。
Humanクラスは「人間」の設計図です。名前、身長、体重、年齢などの情報を持っています。例えばここに「Javaの経験年数」を追加するとしましょう。
public class Human {
private String name;
private double height;
private double weight;
private int age;
private int javaExperienceYears; //java経験年数
このクラス設計は本当に大丈夫でしょうか?確かにプログラマーには必要そうな属性ですが、例えば幼稚園に通っているような小さい子供にこの属性は必要でしょうか?コンピュータのない集落民族の方にも無縁そうです。回りくどくなりましたが、つまり「java経験年数の属性は不必要な人もいるので、Humanクラスに定義するのは不適切」と言いたい。
ではどうすべきなのか?結論から言うと「Programmerクラス」を作ります。
public class Programmer extends Human { //継承を定義するには「extends」を使う
private int javaExperienceYears;
public Programmer(String name, double height, double weight, int age, int javaExperienceYears) {
super(name, weight, weight, age); //superは親クラスのコンストラクタの呼び出し処理(必須)
this.javaExperienceYears = javaExperienceYears;
}
public String teachJavaExperienceYears() {
return "私のJava経験年数は" + javaExperienceYears + "年です。";
}
}
class名を定義する箇所で「extends + 継承元クラス」を追加すると、継承元クラスの属性や操作を継承したクラスを作ることができます。ちなみに継承元クラスを「親クラス」、継承先クラスを「子クラス」と言います。Humanクラスが親で、Programmerクラスが子です。
コンストラクタ内の「super」メソッドは親クラスのコンストラクタを呼び出すために必要です。子クラスは親クラスの特徴を引き継いでいるオブジェクトなので、土台となる親クラスのコンストラクタの呼び出しは必須です。
継承した子クラスも親クラス同様にインスタンスを生成できます。新しく追加したメソッドの呼び出しも可能です。
Programmer pg = new Programmer("佐藤太郎", 170, 62, 20, 1);
System.out.println(pg.teachJavaExperienceYears()); //私のJava経験年数は1年です。
そして、子クラスは親クラスの属性や操作を受け継いでいるので、「親クラスの操作を呼び出すこともできます」。
Programmer pg = new Programmer("佐藤太郎", 170, 62, 20, 1);
System.out.println(pg.teachAge()); //私は20歳です。
(3)なぜ継承するのか
基本的にはソースコードの冗長を抑えるためです。あとは現実に近い実装方法なので、構造的に分かりやすいというメリットもあります。これらはすべて「仕様変更に強い」コードへとつながっています。
特にソースは冗長になりがちです。システムが大規模になるにつれ、似たような機能が乱造される可能性はどんどん増していきます。この場合は同じ機能の部分を親クラスを使って汎化してあげることで冗長化を防げます。機能ごとの細かい違いは子クラスに定義します。
また、しっかりとした継承関係にあるクラス設計ができていると、例えば親クラスに新たなメソッドを追加すると、子クラスでも同じように使うことができます。少ないソースコードで多くのクラスの振る舞いを追加できるので、これも冗長化を防ぐことにつながります。
今回はプログラマのクラスを新たに作りましたが、これ以外にも営業職のクラスや建設業のクラスを作ることももちろん可能です。また職業だけでなく、大学生クラスや社会人クラスといった特化の仕方もありますね。現実に即した構造を保つことで、直観的に分かりやすい設計ができます。
2.まとめ
今回は継承について、基本的な部分を解説しました。継承もカプセル化と同じく、しなくても動くプログラムは作れるけど、しながら作ることでメリットが生まれます。うまく活用できるようになることで、プログラム設計に強いプログラマーになることができます。
次回は「ポリモーフィズム」について学習していきます。ポリモーフィズムを理解するためにはカプセル化や継承を理解している必要があります。ちょっと自信がない方は、もう一度見直してから挑戦してみても良いでしょう。