なぜ継承が必要なのか
ここまではクラスの使い方を学びました。
例えばHumanクラスというものを使用していましたが、場合によってはこのクラスをちょっと改良したい、という場合があります。
Humanクラスの機能は使えるので問題ないが、もう少し機能が欲しいという感じです。
例えば、Salarymanというクラスが欲しかったとします。
【継承を使わない例】
Humanクラス
public class Human{ private String name; private int age; private double height; public Human( ){ System.out.println("引数なしコンストラクタが呼ばれました!"); } public Human(String n, int a, double h){ this( ); System.out.println("引数ありコンストラクタが呼ばれました!"); name = n; age = a; height = h; } public void setName(String n){ name = n; } public void setAge(int a){ age = a; } public void setHeight(double h){ height = h; } public void selfIntroduce( ){ System.out.println("私の名前は" + name + "です"); System.out.println("私の年齢は" + age + "です"); System.out.println("私の身長は" + height + "です"); } }
Salarymanクラス
public class Salaryman { private String name; private int age; private double height; private int salary; private int bonus; public Salaryman( ){ System.out.println("引数なしコンストラクタが呼ばれました!"); } public Salaryman(String n, int a, double h, int s, int b){ this( ); System.out.println("引数ありコンストラクタが呼ばれました!"); name = n; age = a; height = h; salary = s; bonus = b; } public void setName(String n){ name = n; } public void setAge(int a){ age = a; } public void setHeight(double h){ height = h; } public void selfIntroduce( ){ System.out.println("私の名前は" + name + "です"); System.out.println("私の年齢は" + age + "です"); System.out.println("私の身長は" + height + "です"); System.out.println("私の年収は" + getIncome( ) + "です"); } public int getIncome(){ return salary * 12 + bonus * 2; } }
サラリーマンも人間であることには変わりがないので、Human.javaのソースコードをそのまま流用して新しいクラスを作ってみたとします。
しかし、基本的にこういう作り方は行ってはいけません。
同じ内容のソースコードが2箇所に書いてある、というのはクラスとかオブジェクト指向云々に関係なく、プログラミングにおいては良くない書き方です。
ポイント!同じコードを繰り返し書いてはいけない
なぜでしょうか。
このようにコピーをしてフィールドやメソッドを追加したクラスを使っていて、ある日Humanクラスの内容を変更したくなったとします。
4行目で使っている、身長を表すheightというフィールドはdouble型です。
これをfloat型に変えよう、などという変更があるかもしれません。
ではHumanクラスの内容を変えた場合、Salarymanクラスにもフィールドheightが存在しますが、まだdoubleのままです。
こちらもfloatに変更しないと、もともとあったHumanクラスと違う使い方になってしまいます。
もともとは1つだった情報を2箇所以上で管理しなければいけない、というのは非常に大変です。
これはプログラムが大規模になると、大変というより破綻してしまうでしょう。それ以外にも、単純にソースコードが長くなって読みづらくなるなどの問題も発生します。
こういうことを「プログラムの保守性が悪い」と言います。
このような問題を解決することができるのが、「継承」という機能です。
継承とは
もともとあったクラスの性質をそのままもらうことができ、さらに自分でいろいろ追加できる、というのが継承の役割です。
自分でMyClassを作ったときにすでに便利なクラスGreatClassがあったとして、このGreatClassの機能をそのまま利用することができます。
public class MyClass extends GreatClass{ }
このように記述するだけで、MyClassはGreatClassの機能をすべて持っている事と同じになります。
extendsというキーワードを使って、どのクラスの機能を使うか、を宣言します。
ポイント!スーパークラス
この場合のGreatClassをJavaでは「スーパークラス」と呼びます。
他に、基本クラス、基底クラスなどと呼ばれることもあります。
ポイント!サブクラス
この場合のMyClassをJavaでは「サブクラス」と呼びます。
他に、派生クラスと呼ばれることもあります。
【継承】
public class Salaryman extends Human{ private int salary; private int bonus; public Salaryman( ){ System.out.println("Salarymanクラスの引数なしコンストラクタが呼ばれました!"); } public Salaryman(String n, int a, double h, int s, int b){ System.out.println("Salarymanクラスの引数ありコンストラクタが呼ばれました!"); setName(n); setAge(a); setHeight(h); salary = s; bonus = b; } public int getIncome( ){ return salary * 12 + bonus * 2; } }
public class UseHuman{ public static void main(String[ ] args){ Salaryman taro = new Salaryman("太郎", 30, 175.5, 30, 60); //Humanクラスのメソッド taro.selfIntroduce( ); //Salarymanクラスのメソッド int income = taro.getIncome( ); System.out.println("収入は" + income + "です"); } }
実行結果
引数なしコンストラクタが呼ばれました! Salarymanクラスの引数ありコンストラクタが呼ばれました! 私の名前は太郎です 私の年齢は30です 私の身長は175.5です 収入は480です
Salarymanクラスは、Humanクラスにいくつかフィールドとメソッドを追加しただけのものです。
Humanクラスのフィールドとメソッド、Salarymanクラスで定義したフィールドとメソッドすべてを持ったクラスと考えることができます。
例えば、Humanクラスで定義されたselfIntroduce( )というメソッドも使うことができます。
もちろん、自分のクラスで定義したメソッドも呼び出せます。
extendsというのは「拡張する」という意味です。
もともとあった機能はなくすわけではなく、そのまま使えます。
さらにいろいろな機能を付け加えていくので、拡張だと考えられるのです。
このように、継承したクラスでは継承元のクラスが持っているメンバもすべて持っているとして利用できます。
二者の関係を分かりやすくするために、継承のイメージをよく図で書きあらわします。
よくこういった書き方をします。
もともとあったクラスと、その機能を継承して新しく作ったクラスはそれぞれ名前がついています。
また、最初のうちは間違えて矢印の向きを逆に書いてしまうことが多いので注意してください。
スーパークラスはサブクラスの情報をいっさい知る必要はありません。
サブクラスはスーパークラスがどんなメンバを持っているかを知らなければ使えません。
そういった意味があって矢印はサブクラスからスーパークラスへという方向にします。
「継承 Part2」へ続きます
独学で挫折しそうになったら、オンラインプログラミングスクール