Javaの抽象クラスのコンストラクタとは?初心者向けに使い方と注意点をやさしく解説!
生徒
「先生、Javaの抽象クラスにもコンストラクタってあるんですか?」
先生
「ありますよ。抽象クラスもクラスの一種なので、コンストラクタを定義できます。」
生徒
「でも、抽象クラスってインスタンス化できないですよね?コンストラクタの意味ってあるんですか?」
先生
「いい疑問ですね。実は、抽象クラスのコンストラクタは、サブクラスがインスタンス化されるときに呼び出されるんです。それでは詳しく見ていきましょう。」
1. Javaの抽象クラスにもコンストラクタはある?
Javaの抽象クラス(abstract class)にも、普通のクラスと同じようにコンストラクタを定義することができます。たとえ抽象クラスが直接インスタンス化できなくても、コンストラクタはサブクラスから使われるという重要な役割があります。
抽象クラスのコンストラクタは、共通の初期化処理をまとめる場としてよく使われます。これは、Javaの継承と初期化順序の理解にもつながる非常に大切なポイントです。
2. 抽象クラスのコンストラクタの基本的な書き方
まずは、抽象クラスにコンストラクタを定義する基本的な書き方を見てみましょう。
abstract class Animal {
String name;
// コンストラクタ
public Animal(String name) {
this.name = name;
System.out.println("Animalのコンストラクタが呼び出されました");
}
// 抽象メソッド
abstract void makeSound();
}
上記のように、抽象クラスでもpublicやprotectedなどのアクセス修飾子を使って、コンストラクタを定義できます。
3. サブクラスでの抽象クラスのコンストラクタの使い方
抽象クラスのコンストラクタは、サブクラスのコンストラクタの中でsuper()を使って呼び出します。次のように書きます。
class Dog extends Animal {
public Dog(String name) {
super(name); // 抽象クラスのコンストラクタを呼び出す
}
@Override
void makeSound() {
System.out.println(name + "がワンワンと鳴いています");
}
}
この例では、Dogクラスをインスタンス化すると、まずAnimalクラスのコンストラクタが実行され、そのあとにDogクラスの処理が行われます。
4. 実行してみよう!コンストラクタの呼び出し順を確認
実際にDogクラスを使って、抽象クラスのコンストラクタがどう呼ばれるのか確認してみましょう。
public class Main {
public static void main(String[] args) {
Dog dog = new Dog("ポチ");
dog.makeSound();
}
}
Animalのコンストラクタが呼び出されました
ポチがワンワンと鳴いています
上記のように、サブクラスのコンストラクタが呼ばれると、必ず先に親クラス(抽象クラス)のコンストラクタが実行されます。この初期化の順序を覚えておくと、トラブルを避けやすくなります。
5. 抽象クラスのコンストラクタを使う場面とは?
抽象クラスのコンストラクタは、主に以下のような場面で使います:
- サブクラスで共通に使うフィールドの初期化
- データの検証や準備処理
- ログ出力やデバッグ用の出力
例えば、nameやidのようなプロパティを、全てのサブクラスで共通に扱いたいとき、抽象クラスのコンストラクタで設定しておくと便利です。
6. Javaの抽象クラスとインターフェースの違いとの関係
Javaのインターフェースと抽象クラスの違いのひとつに、コンストラクタの有無があります。
- インターフェースにはコンストラクタを定義できません。
- 抽象クラスはコンストラクタを定義できます。
この違いから、初期化処理が必要な共通ロジックを持たせたいときは、抽象クラスを選ぶのが自然な設計になります。
7. 抽象クラスのコンストラクタに引数がある場合の注意点
抽象クラスのコンストラクタに引数があると、super()で必ず引数を渡す必要があります。忘れるとコンパイルエラーになります。
例えば次のように書いてしまうと、エラーになります:
class Cat extends Animal {
public Cat() {
// エラー:Animal(String) のコンストラクタは引数なしでは呼び出せません
}
}
正しくは以下のようにsuper()に引数を渡します:
class Cat extends Animal {
public Cat(String name) {
super(name); // 必ず引数を渡す
}
@Override
void makeSound() {
System.out.println(name + "がニャーニャーと鳴いています");
}
}
8. 抽象クラスのコンストラクタはオーバーロードも可能
抽象クラスのコンストラクタも、普通のクラスと同じようにオーバーロード(引数の違う複数のコンストラクタ)を定義できます。
abstract class Animal {
String name;
public Animal(String name) {
this.name = name;
}
public Animal() {
this.name = "名前なし";
}
abstract void makeSound();
}
このようにして、引数あり・なしの両方に対応できます。ただし、サブクラスでどちらを呼ぶかはsuper()で明示的に指定しましょう。