Java道|デフォルトコンストラクタと生成ルール

オブジェクトは、ただ作れればよいわけではない。
コンストラクタの生成ルールを知ると、クラスの作られ方まで安全に設計できる。

コンストラクタを学ぶと、オブジェクトを作る瞬間に初期状態を整えられることが分かってきます。

何も指定せずに基本状態の隊士を作ることもできます。
最初から体力や呼吸の力を指定して、準備済みの隊士として作ることもできます。

ここからさらに大切になるのが、「そのクラスは、どのような作り方を許すべきか」という考え方です。

鬼滅の刃でたとえると、隊士を登場させるときのルールを決めるようなものです。

何も指定せず、とりあえず隊士を登場させてもよいのか。
それとも、必ず体力や呼吸の力を指定してから登場させるべきなのか。
基本の登場処理は外から使わせず、正式な作成方法だけを公開するべきなのか。

このような作り方のルールを考えるときに重要になるのが、デフォルトコンストラクタと、public や private を使った生成制御です。

コンストラクタを書かなかったときはどうなるのか

クラスの中にコンストラクタをひとつも書かなかった場合、Javaは引数なしのコンストラクタを自動で用意します。

これをデフォルトコンストラクタといいます。

たとえば、次のような DemonSlayer クラスを考えます。

class DemonSlayer
{
    private int stamina;
    private double breathingEnergy;
}

このクラスには、コンストラクタが書かれていません。

しかしJavaは、内部的には引数なしでオブジェクトを作れる入口があるものとして扱います。

そのため、次のような形でオブジェクトを作れます。

DemonSlayer mizuki = new DemonSlayer();

つまり、デフォルトコンストラクタとは、クラスにコンストラクタを1つも書かなかったときにJavaが自動で用意する、引数なしのコンストラクタです。

状態Javaの扱い
コンストラクタを1つも書いていない引数なしのデフォルトコンストラクタが自動で用意される
new DemonSlayer() と書くオブジェクトを作れる

鬼滅の刃でたとえると、隊士の登場方法を特に決めていない場合に、Javaが「とりあえず登場できる基本入口」を用意してくれるようなものです。

デフォルトコンストラクタは基本の登場口

デフォルトコンストラクタは、細かい指定をしなくてもオブジェクトを作れる基本入口です。

鬼滅の刃でたとえると、まだ名前や体力、呼吸の力を細かく決めていなくても、とりあえず隊士を1人登場させられる入口です。

このとき、フィールドには型に応じた初期値が入ります。

フィールドの型初期値
booleanfalse
char\u0000
整数型0
浮動小数点型0.0
参照型null

たとえば、int 型の stamina は 0 になります。
double 型の breathingEnergy は 0.0 になります。

つまり、何も指定せずに DemonSlayer オブジェクトを作った場合でも、フィールドには最低限の初期値が入ります。

鬼滅の刃でたとえると、隊士は登場できるけれど、まだ細かい情報は整っていない基本状態です。

↓クリックすると拡大表示されます。

いつでも自由に作れることが便利とは限らない

引数なしで簡単にオブジェクトを作れるのは便利です。

しかし、どんなクラスでも「何も指定せずに作れる状態」がよいとは限りません。

たとえば、鬼殺隊士を表すクラスを考えてみましょう。

その隊士が、必ず体力と呼吸の力を持った状態で登場すべきだとします。
それなのに、何も指定せずに作れてしまうと、次のような状態が起こります。

起こりうる状態問題
体力が初期値のまま任務に出せる状態か分かりにくい
呼吸の力が初期値のまま戦える状態か判断しにくい
必要な情報がそろっていないオブジェクトとして不自然な状態になりやすい

鬼滅の刃でたとえると、体力も呼吸の力も確認しないまま、隊士を任務地へ出してしまうようなものです。

このような場合は、「何も指定しない作り方」を外から使わせない設計にしたほうが安全です。

そこで使えるのが、コンストラクタにつける public と private です。

コンストラクタにも public と private をつけられる

public や private は、フィールドやメソッドだけでなく、コンストラクタにもつけられます。

コンストラクタに public をつけると、クラスの外からそのコンストラクタを使ってオブジェクトを作れます。

コンストラクタに private をつけると、クラスの外からそのコンストラクタを使えなくなります。

修飾子コンストラクタにつけたときの意味
publicクラスの外から使える作成入口になる
privateクラスの外から使えない内部専用の作成入口になる

鬼滅の刃でたとえると、public コンストラクタは外に開かれた正式な登場口です。
private コンストラクタは、外からは使えない内部専用の準備部屋です。

この2つを使い分けることで、どの作り方を外に見せるか、どの作り方を隠すかを決められます。

private なコンストラクタは外から使えない

たとえば、引数なしコンストラクタを private にすると、クラスの外から引数なしでオブジェクトを作れなくなります。

class DemonSlayer
{
    private int stamina;
    private double breathingEnergy;

    private DemonSlayer()
    {
        stamina = 0;
        breathingEnergy = 0.0;
        System.out.println("隊士を作成しました。");
    }

    public DemonSlayer(int s, double e)
    {
        this();
        stamina = s;
        breathingEnergy = e;
        System.out.println("体力を" + stamina + "に呼吸の力を" + breathingEnergy + "にしました。");
    }

    public void show()
    {
        System.out.println("体力は" + stamina + "です。");
        System.out.println("呼吸の力は" + breathingEnergy + "です。");
    }
}

このクラスでは、引数なしコンストラクタが private です。

private DemonSlayer()

そのため、クラスの外から次のように作ることはできません。

DemonSlayer mizuki = new DemonSlayer();

これは、何も指定せずに隊士を作る入口を閉じているということです。

鬼滅の刃でたとえると、「基本状態のまま適当に隊士を登場させる入口は、外には公開していない」という状態です。

public なコンストラクタを正式な入口にする

一方で、public なコンストラクタはクラスの外から使えます。

先ほどの例では、次のコンストラクタが public です。

public DemonSlayer(int s, double e)
{
    this();
    stamina = s;
    breathingEnergy = e;
    System.out.println("体力を" + stamina + "に呼吸の力を" + breathingEnergy + "にしました。");
}

これはクラスの外から使えます。

DemonSlayer enma = new DemonSlayer(100, 20.5);

この作り方では、体力と呼吸の力を指定してから隊士を作ります。

作り方使えるか理由
new DemonSlayer()使えない引数なしコンストラクタが private
new DemonSlayer(100, 20.5)使える引数つきコンストラクタが public

つまり、このクラスでは「何も指定せずに作る方法」は外に公開せず、「体力と呼吸の力を指定して作る方法」だけを正式な入口として公開しているわけです。

これが、コンストラクタを使った生成制御です。

生成方法を限定すると安全な状態を作りやすい

生成方法を限定する目的は、ただ作り方を減らすことではありません。

大切なのは、不自然な作り方を防ぐことです。

たとえば、DemonSlayer クラスでは、隊士を作るときに必ず体力と呼吸の力を指定させたいとします。

その場合、public な引数つきコンストラクタだけを外に公開すれば、外部のコードは必ず次のように作ることになります。

new DemonSlayer(100, 20.5)

これにより、少なくとも作成時に必要な情報を渡す流れを作れます。

設計作られる状態
引数なしの作成を許す必要な情報が未設定のままになる可能性がある
引数つきの作成だけ許す作成時に必要な情報を受け取れる

鬼滅の刃でたとえると、隊士を任務地へ出す前に、体力と呼吸の力を必ず確認するようなものです。

作り方を制限することは、不便にするためではありません。
安全な状態でオブジェクトを作るためです。

private なコンストラクタはクラスの中では使える

private なコンストラクタは、クラスの外からは使えません。

しかし、同じクラスの中からは使えます。

ここがとても大切です。

たとえば、public な引数つきコンストラクタの中で this() を使うと、private な引数なしコンストラクタを呼び出せます。

public DemonSlayer(int s, double e)
{
    this();
    stamina = s;
    breathingEnergy = e;
    System.out.println("体力を" + stamina + "に呼吸の力を" + breathingEnergy + "にしました。");
}

この this(); は、同じクラス内の引数なしコンストラクタを呼び出します。

private DemonSlayer()
{
    stamina = 0;
    breathingEnergy = 0.0;
    System.out.println("隊士を作成しました。");
}

つまり、引数なしコンストラクタは外からは使えません。
でも、クラスの中では共通の初期化処理として使えます。

場所private コンストラクタを使えるか
クラスの外使えない
同じクラスの中使える

鬼滅の刃でたとえると、基本登場処理は外部の人には使わせません。
しかし、正式な登場方法の中では、内部専用の準備儀式として使えるようにしているイメージです。

this() と private コンストラクタの組み合わせ

private コンストラクタと this() を組み合わせると、設計がかなり整理されます。

private な引数なしコンストラクタに、共通の初期化処理をまとめます。

たとえば、次のような処理です。

体力を基本値にする
呼吸の力を基本値にする
隊士を作成しました。と表示する

そして、public な引数つきコンストラクタでは、最初に this() を使ってその共通処理を呼びます。

その後で、受け取った値を使って最終的な状態を整えます。

順番処理
1public な引数つきコンストラクタが外から呼ばれる
2this() で private な引数なしコンストラクタを呼ぶ
3共通の初期化処理を行う
4public コンストラクタに戻る
5受け取った値で体力と呼吸の力を設定する

この形にすると、共通処理を1か所にまとめつつ、外からは正しい作り方だけを使わせられます。

鬼滅の刃でたとえると、外からは正式な登場口だけを使わせます。
その内部では、まず準備部屋で共通の支度を行い、そのあとで体力や呼吸の力を指定値に整えます。

デフォルトコンストラクタが用意される条件

デフォルトコンストラクタについて、もうひとつ大切なルールがあります。

デフォルトコンストラクタは、クラスの中にコンストラクタを1つも定義しなかったときだけ自動で用意されます。

自分でコンストラクタを1つでも定義すると、Javaはデフォルトコンストラクタを自動では用意しません。

クラスの状態デフォルトコンストラクタ
コンストラクタを1つも書いていない自動で用意される
コンストラクタを1つでも書いている自動では用意されない

ここはとても重要です。

たとえば、引数つきコンストラクタだけを自分で書いた場合、引数なしコンストラクタは自動では用意されません。

そのため、引数なしで作れるつもりで次のように書いても、使えない場合があります。

new DemonSlayer()

必要なら、引数なしコンストラクタも自分で書く必要があります。

鬼滅の刃でたとえると、登場ルールを自分で1つでも決めたなら、Javaは勝手に基本入口を追加してくれません。
基本入口が必要なら、自分で用意する必要があります。

図:デフォルトコンストラクタの生成ルール

↓クリックすると拡大表示されます。

この図が示していること

この図では、デフォルトコンストラクタが自動で生成される場合と、生成されない場合を比較しています。

コンストラクタを1つも書いていない場合、Javaは引数なしのデフォルトコンストラクタを自動で用意します。
そのため、new DemonSlayer() のように引数なしでオブジェクトを作れます。

一方で、コンストラクタを1つでも自分で書いた場合、Javaはデフォルトコンストラクタを自動では用意しません。

ここから分かるのは、引数なしで作れるかどうかは、コンストラクタを自分で定義しているかどうかに大きく関係するということです。

コンストラクタ設計は登場ルールを決めること

コンストラクタを設計することは、オブジェクトの作り方を決めることです。

鬼滅の刃でたとえると、隊士の登場ルールを決めることです。

設計鬼滅の刃でたとえると
引数なしで作れるようにするとりあえず隊士を登場させられる
引数つきで作らせる必要な情報をそろえて登場させる
private コンストラクタにする外からその登場口を使わせない
public コンストラクタにする正式な登場口として公開する

どの作り方を許すかによって、クラスの安全さや使いやすさは変わります。

学習用の小さなクラスなら、引数なしで簡単に作れるほうが分かりやすいことがあります。

しかし、実際の設計では、必要な情報がそろわない状態で作られると困るクラスもあります。

そのような場合は、引数つきコンストラクタを public にし、引数なしコンストラクタを private にすることで、作り方を制限できます。

図:private と public で生成方法を制御する

↓クリックすると拡大表示されます。

この図が示していること

この図では、コンストラクタの公開範囲によって、オブジェクトの作り方を制御する流れを表しています。

private DemonSlayer() は、外から使えません。
そのため、new DemonSlayer() のように何も指定しない作り方は閉じられます。

一方で、public DemonSlayer(int s, double e) は、外から使える正式な生成入口です。
この入口を使えば、体力と呼吸の力を指定した状態でオブジェクトを作れます。

さらに、public コンストラクタの内部からは this() を使って private コンストラクタを呼び出せます。
これにより、共通の初期化処理を内部で再利用しながら、外には安全な作り方だけを公開できます。

生成制御を使うとクラスの意図がはっきりする

生成制御を使うと、「このクラスはどう作られるべきか」という意図が分かりやすくなります。

たとえば、引数なしコンストラクタを public にしておけば、基本状態で作ってよいクラスだと伝わります。

一方で、引数なしコンストラクタを private にし、引数つきコンストラクタだけを public にしておけば、必要な情報を指定して作るべきクラスだと伝わります。

設計クラスの意図
public な引数なしコンストラクタがある基本状態で作ってよい
public な引数つきコンストラクタがある指定値を渡して作ってほしい
private な引数なしコンストラクタがある基本作成は内部専用にしたい
public コンストラクタから private コンストラクタを呼ぶ共通初期化は内部で再利用したい

鬼滅の刃でたとえると、隊士の登場方法に規律を持たせるようなものです。

誰でも自由に隊士を登場させるのではなく、必要な情報をそろえた正式な登場方法だけを認める。
そうすることで、隊士が不自然な状態で現れることを防ぎやすくなります。

デフォルトコンストラクタと生成制御はつながっている

デフォルトコンストラクタと生成制御は、別々の話に見えて、実はつながっています。

デフォルトコンストラクタは、コンストラクタを何も書かなかったときにJavaが自動で用意する基本入口です。

しかし、自分でコンストラクタを書いた時点で、その自動生成は止まります。

つまり、コンストラクタを自分で書くということは、オブジェクトの作り方を自分で設計し始めるということです。

段階意味
コンストラクタを書かないJavaが基本入口を用意する
コンストラクタを書く作り方を自分で決める
public をつける外から使える入口にする
private をつける外から使えない入口にする

鬼滅の刃でたとえると、何も登場ルールを決めなければ、基本入口が自動で用意されます。
しかし、自分で登場ルールを作ったなら、その後はどの入口を開くか、どの入口を閉じるかを自分で決める必要があります。

この考え方が分かると、コンストラクタはただの初期化処理ではなく、オブジェクトの生まれ方を設計する道具として見えてきます。

いちばん大事な感覚

デフォルトコンストラクタと生成ルールで大切なのは、次の感覚です。

ポイント内容
デフォルトコンストラクタコンストラクタを1つも書かないときに自動で用意される引数なしコンストラクタ
自動生成の条件自分でコンストラクタを1つでも書くと、自動では用意されない
public コンストラクタクラスの外から使える生成入口
private コンストラクタクラスの外からは使えない内部専用の生成入口
生成制御どの作り方を外に許すかを決めること
this() との組み合わせprivate コンストラクタを内部で再利用できる

鬼滅の刃でたとえると、コンストラクタ設計は隊士の登場ルールを決めることです。

何も指定せずに登場できる基本入口を開くのか。
必ず体力や呼吸の力を指定して登場させるのか。
基本準備は内部専用にして、正式な登場口だけを外に開くのか。

こうしたルールを決めることで、クラスはより安全で、意図が伝わりやすい部品になります。

コンストラクタは、オブジェクトを作るための入口です。
その入口をどう開き、どう閉じるかを考えられるようになると、クラス設計の見え方が一段深くなります。