コンストラクタ: オブジェクト誕生の秘密

docs

以前の記事では、クラスが「設計図」で、オブジェクトがその設計図から作られた「実体」だと説明しましたね。

クラスとオブジェクトの概念 – オブジェクト指向の入り口 | ToolDocs

今回は、そのオブジェクトがどうやって生まれるのか、その誕生の秘密を握る「コンストラクタ」について解説していきます。

コンストラクタって何?

コンストラクタは、一言でいうと「オブジェクトを初期化するための特別なメソッド」です。

「初期化」って難しく聞こえるかもしれませんが、要するに「オブジェクトが生まれたときに、最初の状態を設定してあげること」だと思ってください。

例えば、新しい車を買ったとします。その車は、最初から色が決まっていて、エンジンも搭載されていますよね? 買った後に色を塗ったり、エンジンを載せたりするわけではありません。コンストラクタは、オブジェクトが「生まれてくる」瞬間に、まさにその「色」や「エンジン」にあたる情報を設定してくれる、そんな役割を担っています。

Javaでは、newキーワードを使ってオブジェクトを作成しますよね。

Java

Car myCar = new Car();

このnew Car()の部分が、実はコンストラクタを呼び出しているんです。

コンストラクタの書き方

コンストラクタには、いくつかルールがあります。

  1. メソッド名がクラス名と同じ: これが一番の特徴です。
  2. 戻り値の型がない: voidも書きません。
  3. アクセス修飾子はつけられる: publicprivateなどを指定できます。

例を見てみましょう。前回の記事で登場したCarクラスにコンストラクタを追加してみます。

Java

class Car {
    String color; // 車の色
    int speed;    // 車の速度

    // コンストラクタ
    public Car() {
        this.color = "白"; // 初期の色を白に設定
        this.speed = 0;   // 初期速度を0に設定
        System.out.println("新しい車が誕生しました!色は" + this.color + "です。");
    }

    // メソッド(前回のおさらい)
    public void accelerate() {
        speed += 10;
        System.out.println("加速!現在の速度は" + speed + "km/hです。");
    }

    public void brake() {
        speed -= 10;
        System.out.println("減速!現在の速度は" + speed + "km/hです。");
    }
}

public class Main {
    public static void main(String[] args) {
        Car myCar = new Car(); // コンストラクタが呼ばれる
        System.out.println("私の車の色: " + myCar.color);
        myCar.accelerate();
    }
}

このコードを実行すると、

新しい車が誕生しました!色は白です。
私の車の色: 白
加速!現在の速度は10km/hです。

と表示されます。new Car()とした瞬間に、Carクラス内のコンストラクタが呼ばれ、colorが”白”、speed0に初期化されているのが分かりますね。

paizaで実行した結果

引数ありコンストラクタ: オブジェクトをカスタマイズ!

先ほどの例だと、どんな車を作っても色が”白”になってしまいます。でも、車って色々な色がありますよね? 買ったときに色を選びたい! そんなときに使うのが、「引数ありコンストラクタ」です。

コンストラクタも通常のメソッドと同じように、引数を受け取ることができます。これを利用すれば、オブジェクトを作る際に初期値を指定できるようになります。

Java

class Car {
    String color;
    int speed;

    // 引数なしコンストラクタ(デフォルトコンストラクタ)
    public Car() {
        this("白", 0); // 引数ありコンストラクタを呼び出す
        System.out.println("デフォルト色の車が誕生しました!");
    }

    // 引数ありコンストラクタ
    public Car(String color, int speed) {
        this.color = color;
        this.speed = speed;
        System.out.println(this.color + "の車が誕生しました!");
    }

    // メソッドは省略
    public void accelerate() {
        speed += 10;
        System.out.println("加速!現在の速度は" + speed + "km/hです。");
    }

    public void brake() {
        speed -= 10;
        System.out.println("減速!現在の速度は" + speed + "km/hです。");
    }
}

public class Main {
    public static void main(String[] args) {
        Car myRedCar = new Car("赤", 60); // 赤色の車を作成
        System.out.println("私の車の色: " + myRedCar.color);
        myRedCar.accelerate();

        System.out.println("---");

        Car myBlueCar = new Car("青", 30); // 青色の車を作成
        System.out.println("私の車の色: " + myBlueCar.color);
        myBlueCar.accelerate();

        System.out.println("---");

        Car defaultCar = new Car(); // デフォルトの車を作成
        System.out.println("私の車の色: " + defaultCar.color);
    }
}

実行結果はこうなります。

赤の車が誕生しました!
私の車の色: 赤
加速!現在の速度は70km/hです。
---
青の車が誕生しました!
私の車の色: 青
加速!現在の速度は40km/hです。
---
デフォルト色の車が誕生しました!
白の車が誕生しました!
私の車の色: 白

このように、引数ありコンストラクタを使うことで、オブジェクト生成時に様々な初期値を設定できるようになり、より柔軟なオブジェクト作成が可能になります。

コンストラクタのオーバーロード

先ほどの例のように、引数の数や型が異なる複数のコンストラクタを定義することを「コンストラクタのオーバーロード」と呼びます。

これは、同じ名前のメソッドを複数定義できる「メソッドのオーバーロード」と同じ考え方です。Javaは、newキーワードの後に渡された引数の型や数を見て、どのコンストラクタを呼び出すかを自動的に判断してくれます。

例えば、Carクラスに、色だけを指定するコンストラクタを追加してみましょう。

Java

class Car {
    String color;
    int speed;

    // 引数なしコンストラクタ
    public Car() {
        this("白", 0); // デフォルト値を設定
    }

    // 色だけを指定するコンストラクタ
    public Car(String color) {
        this(color, 0); // 色と速度0で呼び出す
        System.out.println(this.color + "の車が誕生しました!");
    }

    // 色と速度を指定するコンストラクタ
    public Car(String color, int speed) {
        this.color = color;
        this.speed = speed;
        System.out.println(this.color + "の車が誕生しました!");
    }

    // メソッドは省略
    public void accelerate() {
        speed += 10;
        System.out.println("加速!現在の速度は" + speed + "km/hです。");
    }

    public void brake() {
        speed -= 10;
        System.out.println("減速!現在の速度は" + speed + "km/hです。");
    }
}

public class Main {
    public static void main(String[] args) {
        Car myCar1 = new Car();          // 引数なし
        Car myCar2 = new Car("緑");      // 色だけ指定
        Car myCar3 = new Car("黄", 50);  // 色と速度を指定
    }
}

これで、オブジェクトを作る際に、引数の渡し方で様々な初期状態のCarオブジェクトを作れるようになりました。

paizaで実行した結果

デフォルトコンストラクタ

ここまでの説明で、「あれ?コンストラクタを自分で書かなくても、オブジェクトって作れたような…?」と思った人もいるかもしれません。その通りです!

実は、コンストラクタを一つも定義しなかった場合、Javaが自動的に引数のないコンストラクタを作成してくれます。これを「デフォルトコンストラクタ」と呼びます。

Java

class Dog {
    String name;

    // コンストラクタを定義しない
}

public class Main {
    public static void main(String[] args) {
        Dog myDog = new Dog(); // デフォルトコンストラクタが呼ばれる
        myDog.name = "ポチ";
        System.out.println("犬の名前: " + myDog.name);
    }
}

しかし、自分で一つでもコンストラクタを定義してしまうと、Javaはデフォルトコンストラクタを自動生成してくれなくなります。もし、引数ありコンストラクタを定義しつつ、引数なしでもオブジェクトを作成したい場合は、先ほどの例のように引数なしコンストラクタも明示的に定義してあげる必要があります。

paizaで実行した結果

this()とthis.フィールド名の違い

コンストラクタの例で、this()this.colorという書き方がいくつか出てきましたね。これらは似ていますが、役割が異なります。

  • this(): 同じクラスの別のコンストラクタを呼び出すときに使います。コンストラクタの最初の行にしか書けません。重複するコードを避けるのに役立ちます。
  • this.フィールド名: 現在のオブジェクトのフィールドを指します。引数名とフィールド名が同じ場合に、どちらがフィールドかを明確にするためによく使われます。

Java

class Person {
    String name;
    int age;

    // コンストラクタ1
    public Person(String name) {
        this(name, 0); // this()で別のコンストラクタを呼び出し
    }

    // コンストラクタ2
    public Person(String name, int age) {
        this.name = name; // this.nameでフィールドのnameを指す
        this.age = age;
        System.out.println("名前: " + this.name + ", 年齢: " + this.age);
    }
}

public class Main {
    public static void main(String[] args) {
        Person p1 = new Person("太郎");     // コンストラクタ1が呼ばれ、その後コンストラクタ2へ
        Person p2 = new Person("花子", 25); // コンストラクタ2が呼ばれる
    }
}

paizaで実行した結果

コンストラクタを使うメリット

コンストラクタを使うことで、以下のようなメリットがあります。

  • オブジェクトの初期状態を保証できる: オブジェクトが生成された時点で、必要なデータが設定されていることを保証できます。これによって、NullPointerExceptionなどのエラーを防ぎやすくなります。
  • コードの可読性が上がる: オブジェクトがどのような状態で生成されるのか、コードを見ただけで分かりやすくなります。
  • 柔軟なオブジェクト生成: 引数ありコンストラクタやオーバーロードを使うことで、様々な初期値を持つオブジェクトを簡単に作成できます。

まとめ

今回の記事では、Javaプログラミングにおけるコンストラクタについて深く掘り下げてきました。

  • コンストラクタは、オブジェクトを初期化するための特別なメソッド
  • メソッド名がクラス名と同じで、戻り値の型がない。
  • newキーワードでオブジェクトを生成する際に呼び出される。
  • 引数を受け取ることで、オブジェクトの初期値をカスタマイズできる。
  • 引数の数や型が異なる複数のコンストラクタを定義できる(オーバーロード)。
  • コンストラクタを定義しない場合、Javaがデフォルトコンストラクタを自動生成する。
  • this()は別のコンストラクタ呼び出し、this.フィールド名は現在のオブジェクトのフィールドを指す。
  • オブジェクトの初期状態を保証し、コードの可読性と柔軟性を高めるメリットがある。

コンストラクタは、オブジェクト指向プログラミングの根幹をなす非常に重要な概念です。しっかりと理解することで、より堅牢で使いやすいプログラムが書けるようになりますよ。


前回の記事はこちらからどうぞ!

次回の記事では、オブジェクトの「状態」を操作する「ゲッター・セッター」について解説する予定です。お楽しみに!

コメント

タイトルとURLをコピーしました