初心者入門レベルの【Javaの勉強】継承によるクラスの再利用
2020/04/02
今回は、既存の「クラス」に属性や機能を追加して新しい「クラス」を作ることのできる、「継承」という仕組みを勉強します。
これまで、「クラス」を作ってそれを「カプセル化」するなどして、プログラムを部品化し再利用しやすくする方法を勉強しました。
とは言え実際のプログラミングでは、そうして部品化したプログラムをそのまま使えることは少なく、元のプログラムに新しい機能を付け加えるなどの変更を加えたい場合がよくあります。
そうしたときに便利なのがこの「継承」という仕組みで、「継承」によって既に作成したプログラムを機能追加して利用することが容易になるのです。
「継承」とは
それでは「継承」とは何かというと、ある「クラス」を元にして、さらに機能を追加したり拡張したりして新しい「クラス」を作る方法のことです。
この元になる「クラス」のことを「スーパークラス」または「親クラス」と呼び、それから「継承」によって作られる「クラス」を「サブクラス」または「子クラス」と呼びます。
つまり「スーパークラス」に「フィールド」や「メソッド」を追加して、新しく「サブクラス」を作ることが「継承」というわけです。
「スーパークラス」には、Javaのクラスライブラリに標準で用意されている「クラス」を利用することも、自分で作った「クラス」でも利用することもできます。
それでは、実際に以下のような「継承」を利用したプログラムを作ってみましょう。
class SuperClass01 {
void superMethod01() {
System.out.println("スーパークラスのメソッドです。");
}
}
class SubClass01 extends SuperClass01 {
void subMethod01() {
superMethod01();
System.out.println("サブクラスのメソッドです。");
}
}
ここで“SuperClass01”が「スーパークラス」で“SubClass01”が「サブクラス」になりますが、このように「サブクラス」の宣言に“extends”というキーワードを付け、その後ろに「スーパークラス」の名前を記述することで「継承」を利用した「クラス」を作ることができます。
このプログラムでは、“SuperClass01”の「メソッド」の中で、“SuperClass01”の「メソッド」の“superMethod01”を明示的に生成することなく利用しています。
これはJavaの仮想マシンが、「サブクラス」の「インスタンス」を生成するときに、自動的に「スーパークラス」の「インスタンス」も生成するためです。
さらに他の「クラス」からも、「サブクラス」の「変数」や「メソッド」を利用するのと同様に、「スーパークラス」の「変数」や「メソッド」を利用することもできます。
例として、以下のプログラムを作って実行してみましょう。
public class TestExtend01 {
public static void main(String[] args) {
SubClass01 sub = new SubClass01();
sub.superMethod01();
sub.subMethod01();
}
}
この中で、「サブクラス」の“SubClass01”を「インスタンス化」したあと、「スーパークラス」の「メソッド」と「サブクラス」の「メソッド」を順に呼び出しているので、その実行結果は次のようになります。
>java TestExtend01
スーパークラスのメソッドです。
スーパークラスのメソッドです。
サブクラスのメソッドです。
>
このように「継承」を使うことによって、元のプログラムに新しい機能を付け加えるなどの変更を加えたいといった場合に、簡単に対応することができます。
また、「スーパークラス」にバグが見つかった場合でも、それから「継承」によって作った「サブクラス」を変更することなく、「スーパークラス」の変更だけでバグを修正するといった対応もできるようになります。
このように便利な「継承」ですが、「スーパークラス」で“private”で宣言している「変数」や「メソッド」は、それを「継承」している「サブクラス」でもアクセスできないということには注意しましょう。
「継承」できない「クラス」
「継承」して「サブクラス」を作ろうとしても、コンパイル時にエラーになる「クラス」もあります。
それは、宣言に“final”修飾子が付いている「クラス」です。
“final”修飾子は以前「定数」の勉強のときに出てきましたが、「クラス」を“final”で宣言するということは、この「クラス」は完成されていて変更できないものという意味になります。
例えばJavaに標準で用意されているクラスライブラリに文字列を表す“String”の「クラス」がありますが、これも“final”宣言された「クラス」です。
つまり“String”の「クラス」は、Java言語の中で文字列の概念を表すものとして、完成されていて変更できない「クラス」であるということです。
さらに“final”修飾子に関して付け加えると、「スーパークラス」の「メソッド」に“final”修飾子が付いている場合は、「サブクラス」において同じ型で同じ数の引数を持つ同じ名前の「メソッド」を定義することはできません。
つまりその「スーパークラス」の「メソッド」は、「サブクラス」で「オーバーライド(上書き)」できないということです。
#明示されてなくても自動的に「継承」されている場合
Java言語では、“java”パッケージの中の“lang”サブパッケージの中にある“Object”という「クラス」があって、これはすべての「クラス」の「スーパークラス」になっています。
つまり何かの「クラス」を宣言すると、それは意識しなくても自動的に“java.lang.Object”を「継承」した「サブクラス」になるということです。
では上で勉強したように“extends”で明示的に“java.lang.Object”を継承しても同じかというと、もちろん記述としては問題ありませんが、その場合は他の「クラス」が「継承」できなくなるという影響があります。
なぜならJavaでは、一つの「クラス」が複数の「スーパークラス」を持つことを禁止しているからです。
一つの「クラス」が複数の「スーパークラス」を持つことを「多重継承」と呼びますが、これは場合によっては便利な反面、無秩序に利用すると「クラス」のつながりが複雑になって問題が発生しやすくなる危険もあります。
そのためJavaにおいては「多重継承」を禁止して、「単一継承」しかできないような仕様になっています。
「スーパークラス」の「コンストラクタ」
前回「コンストラクタ」を勉強しましたが、それでは「スーパークラス」に「コンストラクタ」があった場合はどうなるでしょうか。
このときの例として、以下のプログラムを作ってみましょう。
class SuperClass02 {
SuperClass02() {
System.out.println("スーパークラスのコンストラクタです。");
}
}
class SubClass02 extends SuperClass02 {
SubClass02() {
System.out.println("サブクラスSubClass02のコンストラクタです。");
}
}
class SubSubClass01 extends SubClass02 {
SubSubClass01() {
System.out.println("サブクラスSubSubClass01のコンストラクタです。");
}
}
public class TestExtend02 {
public static void main(String[] args) {
SubSubClass01 sub = new SubSubClass01();
}
}
このプログラムでは、“SuperClass02”を最初の「スーパークラス」として、“SubClass02”と“SubSubClass01”の順に「継承」していて、それぞれの「クラス」には「コンストラクタ」が定義されています。
“main”の「メソッド」を持つ“TestExtend02”では、“SubSubClass01”を「インスタンス化」しているだけですが、このプログラムを実行すると次のような結果になります。
>java TestExtend02
スーパークラスのコンストラクタです。
サブクラスSubClass02のコンストラクタです。
サブクラスSubSubClass01のコンストラクタです。
>
これを見ると、トップの「スーパークラス」の「コンストラクタ」から順番に実行されていることが分かります。
ではここで「スーパークラス」の「コンストラクタ」に引数を渡したい場合についてですが、このときは「サブクラス」の「コンストラクタ」で“super”というキーワードを使って値を渡します。
例として、以下のプログラムを作ってみましょう。
class SuperClass03 {
SuperClass03(String param) {
System.out.println(param);
}
}
class SubClass03 extends SuperClass03 {
SubClass03() {
super("SuperClass03の引数です。");
System.out.println("サブクラスSubClass03のコンストラクタです。");
}
}
public class TestExtend03 {
public static void main(String[] args) {
SubClass03 sub = new SubClass03();
}
}
これをコンパイルして実行すると次のようになって、「サブクラス」から「スーパークラス」へ引数が渡されていることが分かります。
>java TestExtend03
SuperClass03の引数です。
サブクラスSubClass03のコンストラクタです。
>
また、この“super”は「スーパークラス」から生成した「インスタンス」の参照値を持つ変数として利用することもできます。
つまり「スーパークラス」と「サブクラス」の両方に同じ名前の変数があった場合、以下のプログラムのように“super”を使うことでそれぞれを区別して使うことができます。
class SuperClass04 {
int a = 10;
}
class SubClass04 extends SuperClass04 {
int a = 2 0;
void subMethod04() {
System.out.println("スーパークラスの変数aの値:" + super.a);
System.out.println("サブクラスの変数aの値:" + a);
}
}
public class TestExtend04 {
public static void main(String[] args) {
SubClass04 sub = new SubClass04();
sub.subMethod04();
}
}
このプログラムを実行すると次のような結果になって、「スーパークラス」と「サブクラス」の変数がそれぞれ区別できていることが確認できます。
>java TestExtend04
スーパークラスの変数aの値:10
サブクラスの変数aの値:20
>
まとめ
今回はJavaの「継承」について勉強しました。
この「継承」という仕組みを使うことで、「クラス」を再利用がよりやりやすくなります。
ただし「継承」は「クラス」の再利用だけを目的とするものではなく、「ポリモーフィズム」というオブジェクト指向の概念を理解する際に重要なポイントともなっています。
「ポリモーフィズム」については追って勉強したいと思いますが、そのためにも「継承」についてしっかりとここで理解しておきましょう。
それでは、今回はここまでです。
《 【Javaの勉強】一覧 》