「継承」とは既存のクラスの機能を再利用し、新しいクラスを作成するための重要なオブジェクト指向プログラミングの概念です。
ザックリいえば、クラスを進化させて新たなクラスを作りますよ!というのが継承です。
「継承」を理解し適切なコーディングを行うことで、コード再利用性の向上、メンテナンスの容易化などが可能になります。このページでは具体的なサンプルコードを用いながら継承の概念を理解し、継承を利用する際のコツや注意点をご説明します。
関連 Javaの1stステップ:基本的な構文ルールを1分で復習!
継承とは?
継承とは、既存のクラスの機能や特性を引き継いで新しいクラスを作ることを指します。既存のクラスを「親クラス or スーパークラス」と呼び、そこから派生して作られた新しいクラスを「子クラス or サブクラス」と呼びます。継承を利用すると、親クラスの持つメソッドやフィールドをそのまま使用できるため、コードを一から書く必要がなくなり、非常に便利です。
例えば「動物」という親クラスがあり、その中に「食べる」という動作を定義しているとします。ここで「動物」クラスを継承して「犬」という子クラスを作成すると、「犬」クラスでも「食べる」という動作をそのまま利用することができます。また、「犬」クラスに特有の「吠える」という動作を追加することもできます。
継承を使うことで、共通の機能を親クラスにまとめ個々のクラスにはそのクラス固有の機能だけを追加する、ということができるようになります。こうすることで、コードの再利用性が高まり、変更が必要な場合も親クラスだけを修正すれば良いためメンテナンスが非常に楽になります。
つまるところ、継承はオブジェクト指向プログラミングの超・重要な概念なので、継承をうまく利用できないと、効率的なプログラム作成はできないということになります。
実際にイメージを深めるためには実際のコードを確認していくのが良いので、ここからは早速継承を利用したクラスの定義方法を解説していきます。
継承の構文ルール:extends
Javaで継承を使う際の基本的な構文は非常にシンプル。新しいクラスを定義するときに、既存のクラスを継承するためにextends
キーワードを使用するだけ。
class 子クラス extends 親クラス { // 子クラスのフィールドとメソッド }
具体的なサンプルコードを見てみましょう。まず「動物」という親クラスを作成します。このクラスには「食べる」というメソッドが含まれています。
class Animal { void eat() { System.out.println("This animal eats."); } }
次にこの「動物」クラスを継承して「犬」という子クラスを作成します。このタイミングでextends
キーワードを使用して、継承元の親クラスを指定します。
class Dog extends Animal { void bark() { System.out.println("The dog barks."); } }
この例では「犬」クラスは「動物」クラスを継承しています。そのため「犬」クラスは「動物」クラスの「食べる」メソッドをそのまま使用することができます。同時に「犬」クラスには「吠える」という新しいメソッドを利用できるようになります。
実際にこの「犬」クラスを使ってみると、以下のようになります。
public class Main { public static void main(String[] args) { Dog myDog = new Dog(); myDog.eat(); // 継承した「食べる」メソッドを呼び出す myDog.bark(); // 新しく追加した「吠える」メソッドを呼び出す } } // 出力結果 // This animal eats. // The dog barks.
このように、継承を使うことで親クラスの機能をそのまま再利用しつつ新しい機能を追加することができます。これによりコードの重複を避け、保守性の高いプログラムを作成することができます。
継承の構文は簡潔でわかりやすいため、オブジェクト指向プログラミングを学ぶ上で非常に重要な要素です。継承を正しく理解し、適切に活用することで、効率的で拡張性の高いプログラムを書くことができます。
ここから、継承に関する実践的な応用知識をご説明していきます。
メソッドのオーバーライド
メソッドのオーバーライドとは、親クラスに定義されたメソッドを子クラスで再定義することを指します。→つまり、子クラスは親クラスの基本的な動作を変更したり、拡張したりすることができるということ。
オーバーライドを行うためには、親クラスと同じメソッド名、同じ引数リスト、そして同じ戻り値の型を持つメソッドを子クラスで定義します。また、オーバーライドするメソッドには@Override
アノテーションを付けることが推奨されます。これは、メソッドのオーバーライドが正しく行われているかをコンパイル時にチェックするためです。
- Qアノテーションとは?
- A
Javaプログラムに追加する特別な「注釈」のこと。コードにメタデータ(データに関するデータ)を提供するために使われます。アノテーションを使うと、コードの一部に特定の情報を付加し、その情報を基にコンパイラや実行環境が特定の動作をするように指示できます。
具体的な例を見てみましょう。まず、「動物」という親クラスに「食べる」メソッドを定義します。
class Animal { void eat() { System.out.println("This animal eats."); } }
次に、「犬」という子クラスでこの「食べる」メソッドをオーバーライドしてみます。
class Dog extends Animal { @Override void eat() { System.out.println("The dog eats dog food."); } }
この例では、「犬」クラスは「動物」クラスの「食べる」メソッドをオーバーライドし、犬がドッグフードを食べるという動作を表現しています。
実際にこの「犬」クラスを使ってみると、以下のようになります。
public class Main { public static void main(String[] args) { Dog myDog = new Dog(); myDog.eat(); // オーバーライドした「食べる」メソッドを呼び出す } } // 出力結果 // The dog eats dog food.
このように、親クラスの「食べる」メソッドが「This animal eats.」というメッセージを出力するのに対し、子クラスでオーバーライドされた「食べる」メソッドは「The dog eats dog food.」というメッセージを出力します。
メソッドのオーバーライドを利用することで、親クラスの基本的な動作を引き継ぎつつ、子クラスで特定の動作をカスタマイズすることができます。これにより、プログラムの柔軟性が高まり、特定の状況に応じた動作を簡単に実装することができます。
オーバーライドは、オブジェクト指向プログラミングにおける多態性(ポリモーフィズム)を実現するための重要な手法です。
親クラスのメソッドを再定義することで、コードの再利用性を維持しつつ、必要に応じて動作を変更できるため、非常に強力な機能と言えます。
superキーワードの使用
super
キーワードは、親クラスのメソッドやコンストラクタにアクセスするために使用されます。→つまり、子クラス内で親クラスのメソッドを呼び出したり、親クラスのコンストラクタを明示的に呼び出すことができます。
具体的な例を見てみましょう。まず、いつも通り親クラスに「食べる」メソッドを定義。
class Animal { void eat() { System.out.println("This animal eats."); } }
次に、子クラスで「食べる」メソッドをオーバーライドし、その中でsuper
キーワードを使って親クラスの「食べる」メソッドを呼び出します。
class Dog extends Animal { @Override void eat() { super.eat(); // 親クラスの「食べる」メソッドを呼び出す System.out.println("The dog eats dog food."); // 子クラス独自の処理を追加 } }
この例では、「犬」クラスの「食べる」メソッドの中でsuper.eat()
を呼び出しています。この結果、親クラスの「食べる」メソッドが実行され、その後に子クラス独自の処理が続けて実行されることになります。
実際にこの「犬」クラスを使ってみると、以下の出力が得られます。
public class Main { public static void main(String[] args) { Dog myDog = new Dog(); myDog.eat(); // オーバーライドした「食べる」メソッドを呼び出す } } // 出力結果 // This animal eats. // The dog eats dog food.
このサンプルでは、親クラスの「食べる」メソッドが「This animal eats.」というメッセージを出力し、その後に子クラスの「食べる」メソッドが「The dog eats dog food.」というメッセージを出力します。このように、super
キーワードを使用することで、親クラスのメソッドを呼び出し、その後に子クラスの追加処理を行うことができます。
さらに、super
キーワードは親クラスのコンストラクタを呼び出すためにも使用される場合もあります。親クラスのコンストラクタを呼び出すことで、親クラスの初期化処理を行うことができます。
class Animal { Animal() { System.out.println("An animal is created."); } } class Dog extends Animal { Dog() { super(); // 親クラスのコンストラクタを呼び出す System.out.println("A dog is created."); } }
この例では、親クラスのコンストラクタが先に実行され、その後に子クラスのコンストラクタが実行されます。実際にこのクラスを使ってみると、以下のようになります。
public class Main { public static void main(String[] args) { Dog myDog = new Dog(); // コンストラクタを呼び出す } } // 出力結果 // An animal is created. // A dog is created.
このように、super
キーワードを使うことで、親クラスの初期化処理を確実に行い、子クラスの初期化処理を続けて行うこともできるのがポイント。super
キーワードは、親クラスと子クラスの連携をスムーズにし、コードの再利用性と保守性を向上させる重要な機能です。
継承の制限
Javaの継承にはいくつかの制限があります。
ここでは、主な制限として、final
キーワードの使用と、多重継承の禁止について説明します。
finalキーワード
final
キーワードは、クラスやメソッドを継承したりオーバーライドしたりすることを禁止するために利用されます。以下のようにfinal
を利用することで継承することを禁ずるため、そのクラスを親クラスとして新しいクラスを作ることはできなくなります。
final class Animal { // このクラスは継承できません } class Dog extends Animal { // エラー:Animalクラスはfinalなので継承できません }
同様にメソッドについても、final
を利用することでオーバーライドすることができなくなります。→子クラスでそのメソッドの動作を変更することはできません。
class Animal { final void eat() { System.out.println("This animal eats."); } } class Dog extends Animal { @Override void eat() { // エラー:eatメソッドはfinalなのでオーバーライドできません System.out.println("The dog eats dog food."); } }
これにより、重要なクラスやメソッドの動作が変更されないようにすることができます。
多重継承の禁止
Javaではクラスの多重継承が禁止されています。多重継承とは、1つのクラスが複数のクラスを同時に継承することです。多重継承を許可すると、同じメソッドやフィールドが複数の親クラスに存在する場合にどれを使うべきかが曖昧になるため、Javaではこれを避けるために多重継承を禁止しています。
class Animal { void eat() { System.out.println("This animal eats."); } } class Pet { void play() { System.out.println("This pet plays."); } } class Dog extends Animal, Pet { // エラー:Javaは多重継承をサポートしません }
この制限を回避するために、Javaではインターフェースを使用することができます。インターフェースを用いることで、多重継承に似た効果を実現することができます。
interface Eatable { void eat(); } interface Playable { void play(); } class Dog implements Eatable, Playable { @Override public void eat() { System.out.println("The dog eats dog food."); } @Override public void play() { System.out.println("The dog plays fetch."); } }
このように、implements
キーワードを使って複数のインターフェースを実装することで、多重継承のような効果を得ることができます。インターフェースはメソッドの宣言のみを持ち、実装は各クラスが行うため、メソッドの競合が発生しません。
これらの制限を理解し、適切に活用することで、Javaの継承機能を正しく使いこなすことができます。final
キーワードとインターフェースを活用して、安全で効率的なプログラムを作成しましょう。