継承について~Java~
はじめに
本記事では、「継承」について解説していきたいと思います。Javaのオブジェクト指向において、「継承」の知識は欠かせないものとなります。まずは継承がどういったものなのか、本記事で理解の参考にしていただければ幸いです。
本題に入る前に
まず、オブジェクト指向について簡単におさらいしておきましょう。
オブジェクト指向とは、開発をおこなう上での考え方として、ひとつひとつの要素を「モノ(=Object)」として捉え、それらを上手く組み合わせることによってひとつのプログラムを作り上げていく方向性を意味します。このオブジェクト指向には大きく分けて3つの要素があり、『継承』、『ポリモーフィズム』、『カプセル化』という要素で分けられています。それでは、オブジェクト指向の要素でもある今回の「継承」に移っていきましょう。
継承とは
一言で、過去に作ったもの(クラス)を流用し、受け継いで新しいもの(クラス)をつくる事を言います。
例えば、Xという車種とYという車種の自動車があったとして、この自動車をそれぞれのコードで書いていく時、”アクセル”メソッドや”ブレーキ”メソッドなどの、共通した内容が生じてくると思います。同じ内容であるにも関わらず、それぞれのクラスファイルに同じコードを書くのはとても非効率的です。また、万が一”アクセル”メソッドに変更が起きた場合、全ての車種の”アクセル”メソッドを書き換えるのは、プロジェクトの規模が大きいものほど現実的ではなくなり、細かなミスも増えることになるでしょう。そこで、この”共通した”内容は別のクラスに分けておき、各車種のクラスは共通部分以外の内容を記述した上で、先程の共通部分は皆で共有することができる、という仕組みが登場します。これが「継承」です。
では、一度下の内容を見てみてください。
・生物←哺乳類←ネコ科←ネコ←マンチカン
このように様々なものは、なにかしらの大きな括りに属し、そこからより具体的なものになっていく構造になっています。上のようなケースを継承にあてはめて考えれば、
「モノ」
「乗り物」
「車」
「〇〇(車種)」
上記のクラスがそれぞれ存在することで、
「乗り物」が「モノ」を継承し、
「車」が「乗り物」を、
「〇〇車種」が「車」を継承する
このような構造に組み立てる事ができます。
また継承される側(例えば車)が、車である以上は必ず持っていなければならない機能”アクセル”と”ブレーキ”メソッドを含めていたとすれば、「〇〇(車種)」は車から継承の手続きさえすれば、車クラスの内容を自身のインスタンス内に含めることができるので、「〇〇車種」は車との差分だけを自身のコード内に記載すればいいわけです。
継承の構文
それでは、継承の基本構文を見ていきましょう。継承関係は、継承される【親クラス/スーパークラス(=車クラス)】と継承する【子クラス/サブクラス(〇〇車種)】といったように呼ぶことができます。
【 class (子クラス名) extends (親クラス名) { (親クラスとの差分) } 】※()は構文に含まれません
ex)【 class JavaCar extends Car { … } 】
実際のコードで見てみましょう。
//親クラス(車)
class Car {
public void accelerator () {
System.out.println("アクセル");
}
public void brake () {
System.out.println("ブレーキ");
}
}
//子クラス(車種)
class JavaCar extends Car{
public void playMusic () {
System.out.println("音楽をかける");
}
}
//Mainクラス
public class Main {
public static void main(String[] args) {
JavaCar x = new JavaCar();
x.accelerator();
x.brake();
x.playMusic();
}
}
↓↓↓
=実行結果=
アクセル
ブレーキ
音楽をかける
上のコード文とその実行結果で分かるように、子クラスのクラス宣言時に親クラスを継承する構文を用いているため、直接子クラスにアクセルやブレーキのメソッドが書かれていなくても、Mainクラスで子クラスをインスタンス化し、アクセル/ブレーキ/音楽をかけるの3つのメソッドを呼び出し、問題なく実行することができています。
このように、継承をしたクラスをインスタンス化すると一体どのような事が起こっているのか、という事ですが、JavaCar(子)クラスをインスタンス化した結果、【Car(親)クラス+その差分】をまとめてひとつのインスタンス(x)に生成されているのです。
オーバーライドとは
「オーバーライド」といって、例えば親クラスの持つアクセルメソッドを子クラスが自分仕様に変えたいと思った時に、アクセルメソッドを”再定義”することが可能です。オーバーライドをするにはいくつかのルールがあります。
・シグニチャ(メソッド名、引数の型、引数の数)が全く同じでなければいけない
・戻り値は全く同じ型か、あるいはそのサブクラス型(継承関係)でなければいけない
・アクセス修飾詞は同じか、あるいはそれよりも公開範囲の広いものでなければいけない
先程のCarクラスのアクセルメソッドをJavaCarクラスで再定義してみましょう。JavaCarクラスに、以下のメソッドを追記してみます。
public void accelerator () {
System.out.println("JavaCar版アクセル");
}
元の親クラスメソッドに引数の指定がなかったため、{}内の処理内容を変えるだけの比較的簡単なオーバーライドになりました。これで先程のMainクラスを同じように実行してみると、
=実行結果=
JavaCar版アクセル
ブレーキ
音楽をかける
このように、問題なくしっかり再定義ができていることがわかります。ここで勘違いしやすいのは、アクセルメソッドを”上書き”しているのではなく、”再定義”しているという事です。そのため、親クラスのアクセルメソッドは上書きされることなくインスタンス(x)の中に存在していますが、ルールに従って再定義されたメソッドに関しては、実際に実行をしたとき、再定義された方が実行されます。