14.

Java コンストラクタ完全ガイド — オーバーロードと初期化

編集
この記事の要点
  • コンストラクタはクラスのインスタンス生成時に呼ばれる特殊メソッド。クラス名と同名戻り値型なし
  • コンストラクタを 1 つも書かないとコンパイラがデフォルトコンストラクタ (引数なし・空) を自動生成
  • 同名で引数違いを複数定義するオーバーロードが可能。this(...) で別コンストラクタへ委譲
  • 継承下では子コンストラクタ先頭で super(...) を呼ぶ。書かないと暗黙の super() が挿入される
  • Java 14 の record は全フィールドを引数に取る正準コンストラクタequals/hashCode/toString を自動生成

コンストラクタとは

コンストラクタはクラスのインスタンスを生成する new 演算子の直後に呼び出される特殊なメソッドです。クラス名と同じ名前を持ち、戻り値型を書かないのが特徴です。フィールドの初期化や、生成時にしか実行できない不変条件のチェックを担います。

public class User {
    private final String name;
    private final int age;

    // コンストラクタ(クラス名と同じ・戻り値なし)
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

// 利用側
User u = new User("Taro", 30);

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

コンストラクタを一つも定義しないクラスには、コンパイラが引数なし・本体空のデフォルトコンストラクタを自動で挿入します。逆に何か一つでも定義すると、デフォルトは生成されません。

// 何も書かないクラス
public class Empty {
    // コンパイラが以下を挿入する
    // public Empty() {}
}

// 引数ありを書いた瞬間、引数なしは消える
public class User {
    public User(String name) { /* ... */ }
}

new User();          // ❌ コンパイルエラー(引数なしコンストラクタなし)
new User("Taro");    // ✅ OK

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

同じクラス内に引数の型または数が異なるコンストラクタを複数定義できます。これをオーバーロードと呼びます。共通処理は this(...) で別コンストラクタに委譲するのが定石です。

public class User {
    private final String name;
    private final int age;
    private final String email;

    // 全引数コンストラクタ
    public User(String name, int age, String email) {
        this.name = name;
        this.age = age;
        this.email = email;
    }

    // email を省略 → this(...) で全引数版に委譲
    public User(String name, int age) {
        this(name, age, "");
    }

    // 名前だけ
    public User(String name) {
        this(name, 0, "");
    }
}

super(...) と継承

子クラスのコンストラクタは、本体先頭で必ず親のコンストラクタを呼びます。明示的に super(...) を書かないと、コンパイラが暗黙的に super() (引数なし) を挿入します。親に引数なしコンストラクタが無い場合は明示的に書く必要があります。

public class Animal {
    private final String name;
    public Animal(String name) { this.name = name; }
}

public class Dog extends Animal {
    private final String breed;

    public Dog(String name, String breed) {
        super(name);   // ← 必須(Animal に引数なしコンストラクタが無いため)
        this.breed = breed;
    }
}

final フィールドの初期化

final 修飾されたフィールドはコンストラクタ終了時までに 1 度だけ代入する必要があります。これによりスレッドセーフな不変オブジェクト (immutable) が作れます。

public final class Point {
    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
        // ここで x, y は二度と変更できない
    }

    public int getX() { return x; }
    public int getY() { return y; }
}

private コンストラクタ(Singleton / ユーティリティ)

コンストラクタを private にすると、クラス外部から new できなくなります。Singleton パターンやユーティリティクラスでよく使われます。

// Singleton
public class Config {
    private static final Config INSTANCE = new Config();
    private Config() {}                           // 外部から生成不可
    public static Config getInstance() { return INSTANCE; }
}

// ユーティリティクラス(static メソッドのみ)
public final class StringUtils {
    private StringUtils() {
        throw new AssertionError("インスタンス化禁止");
    }
    public static boolean isEmpty(String s) { return s == null || s.isEmpty(); }
}

Java 14+ record の自動コンストラクタ

Java 14 で正式導入された record は、宣言したフィールドに対応する正準コンストラクタと getter、equals/hashCode/toString を自動生成します。バリデーションを足したい場合はコンパクトコンストラクタを書きます。

// これだけで User(String, int) と name(), age(), equals, hashCode, toString が生える
public record User(String name, int age) {

    // コンパクトコンストラクタ(引数リスト省略)
    public User {
        if (age < 0) throw new IllegalArgumentException("age >= 0");
        if (name == null || name.isBlank()) throw new IllegalArgumentException("name required");
    }
}

User u = new User("Taro", 30);
u.name();   // "Taro"
u.age();    // 30

修飾子とアクセス制御の比較

修飾子外部からの new同パッケージ子クラス内 super(...)
public
protected不可
(なし / package-private)不可同パッケージのみ
private不可不可不可(継承不可)

よくある落とし穴

  • 戻り値型を書いてしまう: public void User() {...} はコンストラクタではなく単なるメソッド。new 時に呼ばれない
  • this(...) と super(...) を同時に書く: コンパイルエラー。どちらか一方かつ先頭行のみ
  • コンストラクタ内でオーバーライド可能メソッドを呼ぶ: 子クラスの未初期化フィールドを触る恐れあり
  • final フィールド未初期化: コンストラクタ全パスで代入が必要

FAQ

Q: コンストラクタから例外を投げてもいい?
A: 問題ありません。バリデーションで IllegalArgumentException を投げるのは定石。ただしリソース確保後に投げると finalize が走らないので、try/catch で確実にクローズを。

Q: 親クラスのコンストラクタを 2 つ以上呼びたい
A: 不可。super(...) は 1 度しか呼べません。設計を見直してファクトリメソッドに分けるのが定石。

Q: Lombok の @AllArgsConstructor とは?
A: 全フィールドを引数に取るコンストラクタを自動生成するアノテーション。@NoArgsConstructor / @RequiredArgsConstructor も併用可能。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 基本的なルール
  2. データ型
  3. 変数
  4. 定数
  5. 配列
  6. コレクション(List,Set,Queue)
  7. Map(連想配列)
  8. 演算子
  9. 条件分岐
  10. 繰り返し制御文
  11. クラス
  12. メソッド
  13. インスタンス化
  14. コンストラクタ
  15. staticキーワード
  16. オーバーロード
  17. 継承
  18. オーバーライド
  19. this
  20. super
  21. パッケージ
  22. アクセス修飾子
  23. 抽象クラス・メソッド
  24. インターフェース
  25. カプセル化
  26. データベース接続
  27. セッション
  28. ファイル入出力
  29. ラムダ式