PR

Java:privateコンストラクタの使い方/使いどころを3分で

Java

Javaのクラスにおけるコンストラクタはインスタンスを生成するときに呼び出される特別なメソッドです。通常はpublicdefault(パッケージプライベート)などで公開されており、他のクラスからnew クラス名()と書くことで自由にインスタンスを作成することができます。

しかし、コンストラクタをprivateにすると、そのクラスの外からはインスタンスを直接生成できなくなります。 これはアクセス修飾子のルールに基づいた制限ですが、このページではなぜそのようなことが起きるか?なぜあえてインスタンス化を禁じる必要があるのか?について1から解説します。

参考 アクセス修飾子とは?

スポンサーリンク

Java:privateコンストラクタとは何か?

Javaのコンストラクタは、newキーワードでインスタンスを生成するときに呼び出される特別なメソッドです。通常はpublicdefault(修飾子無し)により公開されているため、外部のクラスから自由に生成することが可能。

しかし、コンストラクタをprivateで宣言すると、同じクラスの内部以外からは呼び出せなくなります。そのため、他のクラスがnew演算子でインスタンスを勝手に作ることができず、インスタンス生成の流れを制御できるようになります。

ここから順を追ってprivateコンストラクタの定義や使い方・注意点を解説していきます。まずは前提となる「コンストラクタ」って何?から。

ステップ1:コンストラクタの基本役割を理解する

まずは、コンストラクタの基本をおさらいしておきましょう。

  • コンストラクタ名はクラス名と同じ。
  • インスタンス化のタイミングで必ず呼び出される(new クラス名())。
  • フィールドの初期化や、インスタンス作成時の処理を行う場として用いられる。

下記のサンプルでは、publicコンストラクタが定義されており、外部から自由にインスタンスを作成できます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public class Car {
private String brand;
// publicコンストラクタ
public Car(String brand) {
this.brand = brand;
}
public String getBrand() {
return brand;
}
}
public class Car { private String brand; // publicコンストラクタ public Car(String brand) { this.brand = brand; } public String getBrand() { return brand; } }
public class Car {
    private String brand;

    // publicコンストラクタ
    public Car(String brand) {
        this.brand = brand;
    }

    public String getBrand() {
        return brand;
    }
}

この場合、どのクラスからでも

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Car myCar = new Car("Toyota");
Car myCar = new Car("Toyota");
Car myCar = new Car("Toyota");

のようにインスタンスを生成することができます。

ステップ2:アクセス修飾子の復習

Javaには主に4つのアクセス修飾子があります。下記のとおり、メソッドやフィールドだけでなく、コンストラクタにも同様に適用されます。

修飾子アクセスできる範囲
private同じクラスの中のみ
(default)同じパッケージ内
protected同じパッケージ + サブクラス(継承先クラス)
publicどこからでも
スクロールできます

privateコンストラクタは「同じクラスの中からしか呼べない」ため、外部のクラスから直接インスタンスを生成することが不可能になります。

ステップ3:privateにすることで得られる効果

「外部でnew クラス名()できなくなる」ことで、クラス設計上はどんなメリットがあるのでしょうか。代表的な効果としては以下の2点が挙げられます。

  1. インスタンス数の制御が可能
    • 1つだけ(もしくは必要な数だけ)しかインスタンスを作らないように設計できる。
  2. クラスの使い方を限定できる
    • 「インスタンス化して使う」パターンを禁止して、「静的メソッドのみで利用する」など、使い方を明確に制限できる。

ステップ4:主要な活用パターン

privateコンストラクタは、以下のようなケースで特に活用されます。

シングルトン(Singleton)パターン

アプリケーション内でインスタンスを1つだけに制限したいときに使われる定番パターンです。

  • 例:「ログ管理クラス」「設定情報を集約するクラス」など

ユーティリティクラス

すべてのメソッドをstaticで提供し、インスタンス化が意味をなさないクラスです。

  • 例:「算術系メソッドを集めたクラス」「文字列操作をまとめたクラス」など

ファクトリメソッドパターン

インスタンス生成をコンストラクタではなく、静的メソッドを介して行わせるデザインパターンです。

  • 「同じクラスを生成するけど、引数によって異なるサブクラスを返す」など、柔軟な生成ロジックを隠蔽できる。

具体的な実装例

シングルトンのサンプル

シングルトンクラス定義

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public class Singleton {
// 自分自身のインスタンスをstatic領域に保持
private static Singleton instance = new Singleton();
// コンストラクタをprivateにする
private Singleton() {
// 外部からのnewを禁止
}
// インスタンスを取得するpublicメソッド
public static Singleton getInstance() {
return instance;
}
// クラスとしての機能
public void doSomething() {
System.out.println("Singleton instance is working!");
}
}
public class Singleton { // 自分自身のインスタンスをstatic領域に保持 private static Singleton instance = new Singleton(); // コンストラクタをprivateにする private Singleton() { // 外部からのnewを禁止 } // インスタンスを取得するpublicメソッド public static Singleton getInstance() { return instance; } // クラスとしての機能 public void doSomething() { System.out.println("Singleton instance is working!"); } }
public class Singleton {
    // 自分自身のインスタンスをstatic領域に保持
    private static Singleton instance = new Singleton();

    // コンストラクタをprivateにする
    private Singleton() {
        // 外部からのnewを禁止
    }

    // インスタンスを取得するpublicメソッド
    public static Singleton getInstance() {
        return instance;
    }

    // クラスとしての機能
    public void doSomething() {
        System.out.println("Singleton instance is working!");
    }
}

シングルトンクラスの使い方

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public class Main {
public static void main(String[] args) {
// インスタンス生成はprivateコンストラクタで禁止されている
// Singleton s = new Singleton(); // ← これはコンパイルエラー
// getInstance()を通じて取得
Singleton s = Singleton.getInstance();
s.doSomething();
}
}
public class Main { public static void main(String[] args) { // インスタンス生成はprivateコンストラクタで禁止されている // Singleton s = new Singleton(); // ← これはコンパイルエラー // getInstance()を通じて取得 Singleton s = Singleton.getInstance(); s.doSomething(); } }
public class Main {
    public static void main(String[] args) {
        // インスタンス生成はprivateコンストラクタで禁止されている
        // Singleton s = new Singleton(); // ← これはコンパイルエラー

        // getInstance()を通じて取得
        Singleton s = Singleton.getInstance();
        s.doSomething();
    }
}
  • privateなコンストラクタのおかげで、他クラスでnew Singleton()が使えません。
  • 唯一のインスタンスはクラス内部で生成・保持され、getInstance()で取得可能になります。

ユーティリティクラスのサンプル

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public class MathUtil {
// コンストラクタをprivateで宣言し、インスタンス生成を禁止
private MathUtil() {
// 何もしない
}
// 全てstaticメソッド
public static int add(int a, int b) {
return a + b;
}
public static int multiply(int a, int b) {
return a * b;
}
// ... 他にも便利メソッドが続く ...
}
public class Main {
public static void main(String[] args) {
// インスタンス化はできない
// MathUtil util = new MathUtil(); // ← コンパイルエラー
// staticメソッドとして直接使用する
int sum = MathUtil.add(10, 5);
int product = MathUtil.multiply(10, 5);
System.out.println(sum + ", " + product);
}
}
public class MathUtil { // コンストラクタをprivateで宣言し、インスタンス生成を禁止 private MathUtil() { // 何もしない } // 全てstaticメソッド public static int add(int a, int b) { return a + b; } public static int multiply(int a, int b) { return a * b; } // ... 他にも便利メソッドが続く ... } public class Main { public static void main(String[] args) { // インスタンス化はできない // MathUtil util = new MathUtil(); // ← コンパイルエラー // staticメソッドとして直接使用する int sum = MathUtil.add(10, 5); int product = MathUtil.multiply(10, 5); System.out.println(sum + ", " + product); } }
public class MathUtil {
    // コンストラクタをprivateで宣言し、インスタンス生成を禁止
    private MathUtil() {
        // 何もしない
    }

    // 全てstaticメソッド
    public static int add(int a, int b) {
        return a + b;
    }

    public static int multiply(int a, int b) {
        return a * b;
    }

    // ... 他にも便利メソッドが続く ...
}

public class Main {
    public static void main(String[] args) {
        // インスタンス化はできない
        // MathUtil util = new MathUtil(); // ← コンパイルエラー

        // staticメソッドとして直接使用する
        int sum = MathUtil.add(10, 5);
        int product = MathUtil.multiply(10, 5);
        System.out.println(sum + ", " + product);
    }
}
  • 「インスタンスを生成して使う」という意図がないため、privateコンストラクタで誤使用を防ぎます。

ファクトリメソッドのサンプル

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public class ShapeFactory {
// privateコンストラクタ
private ShapeFactory() {}
// ファクトリメソッド(引数によって生成するサブクラスを切り替え)
public static Shape createShape(String type) {
switch(type) {
case "CIRCLE": return new Circle();
case "RECTANGLE": return new Rectangle();
default: throw new IllegalArgumentException("Unknown shape type");
}
}
}
// 利用例
public class Main {
public static void main(String[] args) {
Shape shape1 = ShapeFactory.createShape("CIRCLE");
Shape shape2 = ShapeFactory.createShape("RECTANGLE");
// shape1, shape2は同じShapeインタフェースを実装しているが実態は別
}
}
public class ShapeFactory { // privateコンストラクタ private ShapeFactory() {} // ファクトリメソッド(引数によって生成するサブクラスを切り替え) public static Shape createShape(String type) { switch(type) { case "CIRCLE": return new Circle(); case "RECTANGLE": return new Rectangle(); default: throw new IllegalArgumentException("Unknown shape type"); } } } // 利用例 public class Main { public static void main(String[] args) { Shape shape1 = ShapeFactory.createShape("CIRCLE"); Shape shape2 = ShapeFactory.createShape("RECTANGLE"); // shape1, shape2は同じShapeインタフェースを実装しているが実態は別 } }
public class ShapeFactory {
    // privateコンストラクタ
    private ShapeFactory() {}

    // ファクトリメソッド(引数によって生成するサブクラスを切り替え)
    public static Shape createShape(String type) {
        switch(type) {
            case "CIRCLE": return new Circle();
            case "RECTANGLE": return new Rectangle();
            default: throw new IllegalArgumentException("Unknown shape type");
        }
    }
}

// 利用例
public class Main {
    public static void main(String[] args) {
        Shape shape1 = ShapeFactory.createShape("CIRCLE");
        Shape shape2 = ShapeFactory.createShape("RECTANGLE");
        // shape1, shape2は同じShapeインタフェースを実装しているが実態は別
    }
}
  • ShapeFactoryはprivateコンストラクタであるため、自身のインスタンスを作らせません。
  • かわりに、createShape()という静的メソッドを介して目的のオブジェクト(CircleRectangleなど)を返す仕組みになっています。

実装上の注意点

  1. リフレクションによる呼び出し
    • リフレクション機能を使えば、「技術的には」privateコンストラクタを呼ぶことは可能。
    • しかし通常の使用では想定外であり、セキュリティマネージャの制限も働く場合があるので、基本はリフレクションを使わない設計を想定しましょう。
  2. Javaバージョンや仕様による差異
    • Java 8以降はインタフェースにdefaultメソッドやprivateメソッドを持てるようになったが、これは「実装可否」に関するものです。クラスのprivateコンストラクタとは少し別の文脈です。
  3. 名前の付け方
    • コンストラクタ自体はクラス名と同じなので自由度は少ないですが、getInstance()などのクラスメソッドの名前は明確な目的を示すようにしましょう。
タイトルとURLをコピーしました