検索

キーワード


【Java】継承が与えるメリットや継承方法、注意すべき点を紹介!

  • 公開日:2020-08-28 14:20:07
  • 最終更新日:2021-01-25 19:26:42
【Java】継承が与えるメリットや継承方法、注意すべき点を紹介!

こんにちは、駆け出しプログラマーの若江です!

ここでは初学者として学習を終えた私が、アウトプットの意味も込めて継承とオーバーライドについて紹介させていただきます。

できる限り初学者が理解しやすい内容として紹介させていただくので、参考となれば幸いです!


関連記事リンク:コンストラクタの使い方 / クラスとは? / メソッドの使い方 / インスタンスの生成方法 / アクセス修飾子の使い分け方 / オブジェクト指向(再利用性)の概念 / 引数とは? / final修飾子の使い方 / static修飾子の使い方 / アノテーションとは? / thisとsuperの使い方

継承とオーバーライド

継承とはクラスのメソッドやフィールドの内容を受け継いで、新たなクラスを作成することを指します。

新たなクラスで受け継いだメソッドは中身を作り変えることができ、

メソッドの中身を作り変えることをオーバーライドと呼びます。

ここでは継承とオーバーライドについて紹介させていただきます。


継承の使い方

メソッドやフィールドといったクラスが持つ情報をもとに、

もととなるクラスの情報を持ったまま新たなクラスを作成することを継承と言います。

クラスのメソッドやフィールドを継承すると、

継承元のクラスは「親クラス」もしくは「スーパークラス」、

継承先のクラスは「子クラス」もしくは「サブクラス」と呼ばれ区別されるようになります。

 例) 継承元クラス(親クラス)継承継承先クラス(子クラス)


余談

継承による「親」と「子」の関係を「is-a関係」とも呼びます。

これは子クラスが 1つの親クラスを持つことを示していて、

「子クラス is-a 親クラス」と表すことができるためです。

一方親クラスは複数の子を持つことができるため、「親クラス is-a 子クラス」は成り立ちません。

なぜなら親クラスは、子クラス(A)の場合もあれば子クラス(B)や子クラス(C)の場合もあるためです。


継承のメリット

クラスの継承を行うメリットはコーディングの手間が省略でき、機能が拡張できることです。

通常同じような機能を含むクラスを複数用意する場合、

全てのクラスで同じコーディングを繰り返す必要がありますね。

このような手間を省略するために親クラスを作って共通機能をまとめてしまうことで、

子クラスでは共通機能以外の、子クラス固有の機能だけを書くことができます。

 例) 動物クラス(四足歩行する・食べる)継承犬クラス(お手する) / 猫クラス(爪を研ぐ)


親クラスに共通機能をまとめることでコーディングの手間を省き、

子クラスでは、親クラスの機能を持ちつつ「お手する」や「爪を研ぐ」といった機能を拡張しています。

また、クラス共通のメソッドに変更があった時には、親クラスの修正のみで完結できます。

例えば上記例の「食べる」を「鳴く」へ変更したくなったとします。

 例) 動物クラス(四足歩行する・鳴く)継承 ➡ 犬クラス(お手する) / 猫クラス(爪を研ぐ)


動物の種類が増えれば増えるほど修正作業が増えることを考えると、随分手間が省略されます。

また複数クラスで何度も同じ修正作業を行うと

コーディングミスやミス発見までの労力がかかる恐れも考えられるため、

親クラスにまとめておくメリットは大きいです。


継承方法

親クラスから子クラスへ継承を行うには extends を宣言します。

・継承の基本構文: public class 子クラス名 extends 親クラス名{ 処理内容 }


上記継承の仕方と併せて、子クラスが親クラスのメソッドを継承していることを確認してみましょう。


◆継承のサンプルコード

【親クラス(Animal)】walk メソッドを用意

public class Animal {

	public void walk() {
		System.out.println("歩く");
	}
}

【子クラス(Dog)】Animal クラスの walk メソッドと、Dog クラスの hand メソッドを持つクラス

public class Dog extends Animal {
	public void hand() {
		System.out.println("お手");
	}
}

【実行クラス】Dog クラスのインスタンスを生成、実行

public class Test {

	public static void main(String[] args) {
		Dog dog = new Dog();
		dog.walk(); // 結果:「歩く」を表示
		dog.hand(); // 結果:「お手」を表示
	}
}


Dog クラスでは実装していない walk メソッドが使えています。

これは Dog クラスが Animal クラスを親クラスとして持っているためです。

クラスの親子関係は以下のようなイメージで構成されているため、

親クラスのメソッドへアクセスできるようになっています。

クラスの親子関係のイメージ

このとき、子クラスは親クラスのコピーを持っており、親クラス本体は独立して存在し続けます

この関係は後ほど紹介する「オーバーライド」にも深く関わるため覚えておきましょう。


継承の注意点

・継承をする際は多重継承ができません❌

多重継承とは複数の親クラスの機能を1つの子クラスが1度に受け継ぐことです。

動物クラスが犬クラスや猫クラスを持つように、親クラスが複数の子クラスを持つことはできても、

子クラスが複数の親クラスを持つことはできませんので気を付けましょう。

ただし、継承した子クラスが親クラスとして新たな子クラスへ継承を行うことは可能です。

 例) 動物クラス継承犬クラス(動物クラスの子クラス、柴犬クラスの親クラス)継承柴犬クラス


・コンストラクタは継承されません❌

コンストラクタは、コンストラクタ自身が書かれたクラスの初期化を行う機能です。

上記親子関係の図のように親クラスは継承後も子クラスの中に存在するため、

親クラスが持つコンストラクタは親クラスの初期化を行う役割を果たします。

そのため子クラスは子クラス自身のコンストラクタが必要となります。



オーバーライドの使い方

継承の機能拡張に「オーバーライド」があります。

オーバーライドとはメソッドへ行う拡張機能で、

親クラスを継承した子クラスが親クラスが持つメソッドの中身(動作処理)を書き換えることができます。


オーバーライドのメリット

オーバーライドをすることで、再利用面と保守面でメリットが得られます。

・再利用面

 オーバーライドするとメソッドの活用範囲が広がります。

 例) 動物クラス(四足歩行メソッド(歩く))継承犬クラス(四足歩行メソッド(走る))


 犬クラスは四足歩行メソッドで走ることができるようになりました。

 それだけでなく同じメソッド名で、親クラスが持つ四足歩行メソッドを再利用して歩くこともできます。

・保守面

 親クラスのメソッドの動作処理に変更を加えたくなったとき、

 子クラスへの影響を最小限に抑えることができます。

 例) 動物クラス(四足歩行メソッド(逃げる)) ➡ 継承 ➡ 犬クラス(四足歩行メソッド(走る))


 子クラスは「歩く」を「逃げる」に変更しなくても、親クラスの修正済みメソッド「逃げる」が使えます。

 また逆に親クラスに影響なく子クラスのメソッドを変更することもできます。

 例) 動物クラス(四足歩行メソッド(逃げる)) ➡ 継承 ➡ 犬クラス(四足歩行メソッド(近寄る))


オーバーライドの書き方

オーバーライドは以下3つの条件のもとに書くことができます。

・親子関係にあるクラスの子クラスの中で書く

・メソッド名・戻り値・引数の型・引数の数・引数の並び順は固定(親クラスのメソッドと同形)

・子メソッドのアクセス修飾子(public, protected)のアクセス範囲は親メソッド以上


アクセス修飾子の範囲は親メソッドが public なら(アクセス範囲が最大のため)子メソッドも public、

親メソッドが protected ならオーバーライドした子メソッドは protected か publicです。

親メソッドが private なら子クラスはアクセスできずに子クラス独自のメソッドとして判断されます。


◆オーバーライドの例

【親クラス】

public class Animal {

	public void move() {
		System.out.println("歩く");
	}
}

【子クラス】

public class Dog extends Animal {
	
	// 親クラスの move メソッドをオーバーライド
	public void move() {
		System.out.println("走る");
	}
}


Animal クラスを継承した Dog クラスは、

「Animal クラスの move メソッド」と「Dog クラスの move メソッド」を持っています。

後ほど Dog クラスから Animal クラスの move メソッドへアクセスする方法を紹介します。


オーバーライドの注意点

・static メソッドと final 修飾子のついたメソッドはオーバーライド不可❌

 static メソッドはクラスメソッドの扱いとなるため、オーバーライドできません。

 また final 修飾子のついたメソッドは変更不可となるためオーバーライドできません。

 ※それぞれの詳しい説明は別記事でさせていただきます。

・1つのクラスに2つ以上同名のメソッドはオーバーライド不可

 クラスの基本ルールは1つのクラスに定義できるメソッド名は重複できないルールです。

 ただしオーバーロードしたメソッド名は重複となります。

・オーバーライドとオーバーロードは別機能

 オーバーライドは「親クラスから継承したメソッドの動作処理を子クラスで書き換える」ことで、

 オーバーロードは「引数の異なる同名のメソッドを同クラス内で作成する」ことです。

・抽象クラスの継承は必ずオーバーライドが必要

 抽象クラスについて詳しい紹介は別記事でさせていただきますが、

 オーバーライドが前提として作られるクラスが抽象クラスのため、オーバーライドは必須となります。



子クラスから親クラスへのアクセス方法(super)

子クラスから親クラスへは super を使うことでアクセスすることができます。

ここでは特によく使われるものとして、親クラスのメソッドとコンストラクタを例に紹介したいと思います。


親クラスのメソッド(親メソッド)へのアクセス

通常、継承を行うと親クラスのメソッドを子クラスが引き継ぐため、

先述の例のように普段のメソッドの利用方法で子クラスにある親クラスのメソッドを呼ぶことができます。

しかし子クラスでオーバーライドを行うと、子クラス内に同名のメソッドが2つ存在することになります。

 例) 子クラス(親クラスのメソッドaオーバーライドしたメソッドa)

これでは「子クラスのメソッドa を使ってください」と指示しても、どちらのメソッドa か判断できません。

そしてこの場合、子クラスが持つオーバーライドしたメソッドa を優先的に使用します。

そのため親クラスのメソッドを使用したいときは、

ブロック( { } )内で super を使って明示的に親メソッドを指定しなくてはなりません。

super を使うとオーバーライドしたメソッドを持つ子クラスで、親メソッドを呼ぶことができます。


◆親メソッドの呼び出し例

【親クラス】

public class Animal {

	public void move() {
		System.out.println("歩く");
	}
}

【子クラス】

public class Dog extends Animal {

	// オーバーライドしたメソッド
	public void move() {
		super.move(); // 親メソッドを呼ぶ
		System.out.println("走る");
	}	
}


オーバーライドした子メソッドで、親メソッドの「歩く」を呼んだあと、

「走る」を表示するメソッドが上記の例です。

 ※メソッドは上から順に処理されるため「super.move();」と「System.out.println("走る");」を

  前後で入れ替えると「走る」を表示させたあとに、「歩く」が表示されるようになります。


親クラスのコンストラクタ(親コンストラクタ)へのアクセス

継承を行うと子クラスでは、子クラス独自のコンストラクタを用意する必要がありますが、

子クラスのコンストラクタ内から親クラスのコンストラクタを呼ぶことは可能です。

子クラスから親コンストラクタを呼ぶには、メソッド同様 super を使用します。

子コンストラクタ内で super を宣言し、続いて引数の型、数、順を指定すると、

該当する親コンストラクタへアクセスします。


◆親コンストラクタの呼び出し例

【親クラス】

public class Animal {

	public Animal() {
		System.out.println("親コンストラクタ");
	}
}

【子クラス】

public class Dog extends Animal {

	public Dog() {
		super(); // 引数なしの親コンストラクタを呼び出し
		System.out.println("子コンストラクタ");
	}
}

Dog クラスのインスタンス生成で「親コンストラクタ」次いで「子コンストラクタ」が表示されます。


注意しなければならない点は、メソッドのように super の順番を下げられないという点です。

コンストラクタ内で super を使う場合は最初に宣言するようにしましょう。


親と子のコンストラクタ自動生成

コンストラクタはインスタンス生成時に必ず必要となるため、

クラス内にコンストラクタの記述が無ければ引数なしのコンストラクタが自動生成されます。

先述では継承した子クラスは、子クラス独自のコンストラクタが必要と紹介しましたが、

実は子コンストラクタ内で super を使わない場合でも、親コンストラクタが自動で呼ばれています。

 例) 親クラス(自動コンストラクタ)継承子クラス(自動子コンストラクタ(自動親コンストラクタ))

自動で呼ばれる親コンストラクタも引数なしが呼ばれています。

つまりコンストラクタの記述をしなければ子クラス内でも、子クラスのコンストラクタ内でも、

コンストラクタの自動生成が行われています。

試しに親コンストラクタを作成し、子コンストラクタを自動生成してみましょう。


◆コンストラクタ自動生成の例

【親クラス】初期値で「親コンストラクタ」を表示するコンストラクタ

public class Animal {

	public Animal() {
		System.out.println("親コンストラクタ");
	}
}

【子クラス】子コンストラクタと、子コンストラクタ内の親コンストラクタ自動生成

public class Dog extends Animal {
}

【実行】結果:「親コンストラクタ」を表示します。

public class Test {

	public static void main(String[] args) {
		Dog dog = new Dog();
	}
}


上記を踏まえて、「親コンストラクタ(引数あり)」「子コンストラクタで親コンストラクタ自動生成」で

子クラスを作成しようとすると結果がエラーとなります。

【親クラス】引数ありのコンストラクタ

public class Animal {

	public Animal(String str) {
		System.out.println("親コンストラクタ");
	}
}

【子クラス】エラーとなる

public class Dog extends Animal {
}


これは、親クラスでは引数ありのコンストラクタ( public Animal(String str) )が生成されたため、

引数なしのコンストラクタは自動生成されていません。

しかし子クラスでは引数なしの親コンストラクタ( public Animal(){} )を自動で呼んでいます。

親クラスに引数なしのコンストラクタが無いとエラーを出すためこのような結果となります。


このような場合は親クラスに引数なしのコンストラクタを用意するか、

子クラスで super を使い明示的に引数ありのコンストラクタを呼ぶことで解決できます。


◆子コンストラクタで引数ありの親コンストラクタを呼ぶ例

【親クラス】引数ありのコンストラクタ

public class Animal {

	public Animal(String str) {
		System.out.println("親コンストラクタ");
	}
}

【子クラス】

public class Dog extends Animal {
	public Dog() {
		super("引数ありの親コンストラクタへアクセス");
		System.out.println("子コンストラクタ");
	}
}


これでエラーが解決しました。

継承時のコンストラクタには注意しましょう。



アノテーション

アノテーションは注釈を意味します。

クラスやメソッドなどに対して、どのようなものかを伝える手段として用いられます。

アノテーションには豊富な種類が用意されていますが、

ここでは特に「オーバーライド」のアノテーションについて紹介させていただきます。

オーバーライドのアノテーションをメソッドに付けることで、

「オーバーライドしたメソッドですよ」と伝えることができます。


アノテーションのメリット

クラスやメソッドなどがどういうものかを注釈することで、

プログラムの役割がわかりやすくなり、チーム開発などで他者の書いたプログラムがスムーズに共有されます。

またコンパイラや実行環境も、アノテーションを付けたクラスやメソッドなどがどのようなものかを知るため、

記述ミスなどをコンパイルエラーで警告してくれるなどのメリットが得られます。


アノテーションの書き方

アノテーションはクラスやメソッドなど直前に「アットマーク( @ )」とアノテーション名を書きます。

・アノテーション構文: @アノテーション名


オーバーライドのアノテーションは「@Override」です。

先述した親クラスの move メソッドオーバーライドの例を使いサンプルコードを確認してみましょう。


◆オーバーライドのアノテーション例

public class Dog extends Animal {

	@Override
	public void move() {
		System.out.println("走る");
	}
}


move メソッドがオーバーライドされたメソッドと判断されているため、

もしも誤字をして「nove」と書いた場合も、新たな noveメソッドとは判断されずにエラーを返します。


アノテーションは直後のものの注釈しか行わないため、

複数のものにアノテーションを付けたい場合はその都度アノテーションを付けるようにしましょう。



まとめ

クラスの機能を引き継いで新たなクラスを作ることを継承と呼びます。

在るものを再利用する部分などはポリモーフィズムの考え方にも通じる機能のため、

継承やオーバーライドの機能を押さえておきましょう。


関連記事リンク

コンストラクタの使い方 / クラスとは? / メソッドの使い方 / インスタンスの生成方法 / アクセス修飾子の使い分け方 / オブジェクト指向(再利用性)の概念 / 引数とは? / final修飾子の使い方 / static修飾子の使い方 / アノテーションとは? / thisとsuperの使い方


【著者】

若江

30代で異業種となるIT業界へ転職した駆け出しのプログラマです。これまで主に Java や Ruby、HTML/CSS を使って学習を目的としたショップサイトや掲示板サイトの作成を行いました。プログラマとしての経験が浅いからこそ、未経験者の目線に近い形で基礎の紹介をしていきたいと思います。

よく読まれている記事
【Java】JSPでタグライブラリを使う(JSTL)

【Java】JSPでタグライブラリを使う(JSTL)

こんにちは。エンジニアの新田です!ここでは、システムエンジニアとして働いている私が、システム開発手法や開発言語について紹介していこうと思います。今回は、JSPの標準タグライブラリ「JSTL」について紹介します。Javaについて勉強している方、Webアプリケーションを構築したいと思っている方の参考になれば幸いです!関連記事リンク: 【Java】JSPの基本的な構文/【Java】JSPのアクションタグ

【Java】Stringクラス文字列を操作するメソッドの使い方まとめ!実例も紹介!

【Java】Stringクラス文字列を操作するメソッドの使い方まとめ!実例も紹介!

こんにちは。新人エンジニアのサトウです。システムエンジニアとして駆け出したばかりですが、初心者なりの視点でわかりやすい記事を心がけていますので参考になればうれしいです。プログラム初心者✅にも、プログラムに興味がある人✨も、短い時間で簡単にできますのでぜひこの記事を読んで試してみてください!そもそもStringとは何?『 String 』... Java言語において文字列のデータ型を指します。基本デ

【Java】文字列の置き換え(String#format)!エスケープシーケンスのまとめも!!

【Java】文字列の置き換え(String#format)!エスケープシーケンスのまとめも!!

こんにちは。新人エンジニアのサトウです。システムエンジニアとして駆け出したばかりですが、初心者なりの視点でわかりやすい記事を心がけていますので参考になればうれしいです。プログラム初心者✅にも、プログラムに興味がある人✨も、短い時間で簡単にできますのでぜひこの記事を読んで試してみてください!Stringクラスformatメソッドの文字列整形【java.utilパッケージ】Formatterクラスfo

【Java】文字列格納後に変更可能!?StringBufferクラスとStringBuilderクラス!

【Java】文字列格納後に変更可能!?StringBufferクラスとStringBuilderクラス!

こんにちは。新人エンジニアのサトウです。システムエンジニアとして駆け出したばかりですが、初心者なりの視点でわかりやすい記事を心がけていますので参考になればうれしいです。プログラム初心者にも✅、プログラムに興味がある人✨も、短い時間で簡単にできますのでぜひこの記事を読んで試してみてください!文字列を扱う3つのクラス【java.langパッケージ】java.langパッケージの文字列を扱うクラスにはS

【Java】値?変数?型??しっかり解説!『データ型(プリミティブ型と参照型)』

【Java】値?変数?型??しっかり解説!『データ型(プリミティブ型と参照型)』

こんにちは。新人エンジニアのサトウです。システムエンジニアとして駆け出したばかりですが、初心者なりの視点でわかりやすい記事を心がけていますので参考になればうれしいです。プログラム初心者✅にも、プログラムに興味がある人✨も、短い時間で簡単にできますのでぜひこの記事を読んで試してみてください!プリミティブ型と参照型プログラム開発では型を持った変数を使ってデータのやり取りをしますが、データ型によって仕様