コンストラクタ
コンストラクタ とは
オブジェクトを生まれた瞬間に初期化する仕組み、コンストラクタを学ぼう。new でクラスを呼び出すときに何が起きているのかを追いかけながら、Book クラスを組み立てて本のフルタイトルを返すメソッドを完成させる。
オブジェクトが生まれる瞬間を司る関数
Java でクラスからオブジェクトを作るときに、必ずひと仕事するのが コンストラクタ です。new Book(...) と書いた一瞬の裏側で、Book クラスのコンストラクタが呼ばれ、フィールドが初期化され、ようやく完成したオブジェクトが返ってきます。コンストラクタを正しく書けるようになると、「初期化漏れでバグる」「フィールドが null のままで NullPointerException が出る」といったお決まりの事故を、生まれた瞬間からまとめて防げるようになります。
物作りに例えると、クラスは「家の設計図」、
newは「建設開始のスイッチ」、コンストラクタは「現場監督が最初に行う初期セットアップ」です。土地ならし、基礎打ち、配線 ─ 住人 (=メソッド呼び出し) が入ってくる前に必要な準備を、コンストラクタがまとめて引き受けます。
このレッスンでは、本を表す Book クラスを思い浮かべながら、Solution.bookFullTitle(String title, String author) というメソッドを書いてもらいます。中身では擬似的に Book を組み立てて、title + " by " + author の形のフルタイトル文字列を返します。たった 1 行で済む処理に見えますが、その裏には「フィールドを初期化する責務はどこにある?」というオブジェクト指向の大事な感覚が詰まっています。
コンストラクタとは何か
コンストラクタは、クラス名と同じ名前のメソッドのような存在 で、new でオブジェクトを生成したときに自動的に 1 回だけ呼ばれる特別なメンバーです。普通のメソッドと違うのは次の通りです。
- 名前は クラス名と完全に一致 させる (
BookクラスならBook(...)) - 戻り値の型を書かない (
voidも書かない) returnで値を返さない (返すのはコンストラクタが生成中の自分自身)- 役目は フィールドの初期化 と 生成直後のセットアップ
たとえば本を表す Book クラスは次のように書きます。
Java
public class Book {
String title;
String author;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
}public Book(String title, String author) の行がコンストラクタです。public の次にいきなりクラス名 Book が来て、戻り値の型がどこにも書かれていない、というのが目印です。中身では this.title = title; のように this を使って、引数の title (ローカル変数) とフィールドの title (オブジェクトに属する変数) を区別しています。
thisは「いま生まれようとしている自分自身」を指すキーワードです。コンストラクタ内では引数名とフィールド名を揃えるのが慣習で、こうするとthis.title = title;のように「フィールドの title に、引数の title を入れる」と読めるようになります。
new でコンストラクタが呼ばれる流れ
new Book("Java入門", "著者A") と書いたとき、Java の世界では大まかに次の手順が走ります。
Bookクラスのオブジェクト用にメモリ領域を確保する- すべてのフィールドを デフォルト値 で埋める (
Stringならnull、intなら0) - 引数
"Java入門""著者A"を受け取ったBook(String title, String author)コンストラクタが走る this.titlethis.authorに値が入る- 完成したオブジェクトの参照が
new式の値として返ってくる
この流れを図にすると次のようになります。
この流れを覚えておくと、「new のあとにフィールドが null のまま使われてしまうのはコンストラクタの初期化漏れだ」と素早く気づけるようになります。
デフォルトコンストラクタの自動生成
ここでよくある疑問が「これまでのレッスンではコンストラクタなんて書いていないのに動いていたよね?」というものです。実は Java のコンパイラは、コンストラクタを 1 つも書かなかったクラスに対して、引数なしのコンストラクタを勝手に作って くれます。これを デフォルトコンストラクタ と呼びます。
Java
// 自分では何も書いていない
public class Counter {
int count;
}
// コンパイラがこっそりこう見立ててくれている
public class Counter {
int count;
public Counter() {
// 何もしない
}
}おかげで Counter c = new Counter(); と書ける、というカラクリです。
ただし、引数ありのコンストラクタを 1 つでも自分で定義した瞬間、デフォルトコンストラクタは消えます。これが「いつも
new Book()で動かしていたのに、引数ありコンストラクタを足したらnew Book()がエラーになった」という、初心者あるあるの正体です。両方使いたい場合は、引数なしコンストラクタも自分で書き足します。
Java
public class Book {
String title;
String author;
public Book() {
this.title = "untitled";
this.author = "unknown";
}
public Book(String title, String author) {
this.title = title;
this.author = author;
}
}こんなふうに 同じ名前のコンストラクタを引数違いで複数並べる ことができ、これを オーバーロード と呼びます。Java は引数の数や型を見て、呼び出されたコンストラクタを自動で選んでくれます。
コンストラクタを使ったオブジェクト組み立て例
少し長めの例で、コンストラクタの本領を見ておきましょう。次の Book クラスは、初期化時にタイトルと著者を受け取り、フルタイトル文字列を組み立てて返す fullTitle() メソッドを持っています。
Java
public class Book {
String title;
String author;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
public String fullTitle() {
return title + " by " + author;
}
}
// 使う側
public class Library {
public static void main(String[] args) {
Book b1 = new Book("Java入門", "著者A");
Book b2 = new Book("Effective Java", "Bloch");
System.out.println(b1.fullTitle()); // Java入門 by 著者A
System.out.println(b2.fullTitle()); // Effective Java by Bloch
}
}new Book("Java入門", "著者A") の時点でフィールドが埋まっているので、fullTitle() を呼んだときに null + " by " + null のような事故が起こりません。「使う前に確実に初期化しておく」のがコンストラクタの仕事 だと体で覚えてください。
今回の課題では、Solution.bookFullTitle(String title, String author) の中で同じ感覚を再現します。実際にクラスを切り出してもいいですが、return title + " by " + author; のように直接書いて Book を組み立てたつもりになる、という書き方でも構いません。大事なのは「タイトルと著者という 2 つの情報を受け取って、フルタイトルを返す」という責務を 1 か所に閉じ込めることです。
オブジェクト指向の入り口では、「処理を
staticメソッドにまとめる」のと「クラスを作ってコンストラクタで初期化する」のとの違いに戸惑いがちです。今は「状態を持たない使い捨ての処理ならstaticメソッド、まとまったデータを持って何度も呼ばれるならclass+ コンストラクタ」と覚えておけば十分です。
よくある間違い
コンストラクタまわりで初心者がやらかしがちな落とし穴を 3 つ紹介します。
- コンストラクタに戻り値型を書いてしまう ─
public void Book(...)public String Book(...)のようにvoidやStringを入れてしまうと、Javaのコンパイラはそれを 同じ名前のただのメソッド として扱います。引数付きでnew Book("x", "y")のように呼ぼうとするとコンパイルエラーになります(コンストラクタとして認識されるものが引数なしのデフォルトコンストラクタしかないため)。一方、引数なしでnew Book()と呼んだ場合はデフォルトコンストラクタが動くものの、フィールドは初期化されないままになります。public ClassName(...) {の形を厳守してください - 複数コンストラクタの解決ミス ─
public Book(String title)とpublic Book(String title, String author)の両方を用意した状態でnew Book("Java入門")と呼ぶと、引数 1 個の方が選ばれます。new Book("Java入門", null)を呼ぶと 2 引数の方が選ばれ、authorがnullのままfullTitle()を実行するとnullが出力に混ざります。引数の数と意味は、コンストラクタを増やすたびに丁寧に確認しましょう - デフォルトコンストラクタが消えていることに気づかない ─ 自分で
public Book(String title, String author)を 1 つ追加した瞬間、new Book()は使えなくなります。テストコードや別ファイルでまだnew Book()を呼んでいると、constructor Book in class Book cannot be applied to given typesというエラーが出ます。引数なしも使いたいなら、public Book() {}を明示的に書き足してください
もう 1 つ地味だけど多いのが、this. の付け忘れ です。引数名とフィールド名を同じにしているのに title = title; と書くと、フィールドではなく自分自身に代入することになり、フィールドはずっと null のままです。this.title = title; の this. は飾りではなく、ローカル変数とフィールドを区別するための必須記号 だと覚えてください。
Javaのエラーメッセージは「コンストラクタを呼ぼうとしているのか、メソッドを呼ぼうとしているのか」をconstructormethodという単語で区別してくれます。エラーを読むときは、まずこの 2 つの単語のどちらが書かれているかを見ると、原因を絞り込みやすくなります。
やってみよう
右側のエディタで、Solution.bookFullTitle(String title, String author) を完成させましょう。手順は次のとおりです。
- 引数
String titleとString authorの 2 つを受け取る形になっていることを確認する - メソッドの中身を
return title + " by " + author;に書き換える - 「実行」ボタンを押して、テストがすべて緑になることを確認する
動かせたら、ぜひその次に進んでみてください。たとえば次のような実験です。
- ファイル内で
Bookクラスを自分で書き、new Book(title, author).fullTitle()の戻り値をreturnする書き方に変えてみる bookFullTitle("Java入門", null)のようにnullを渡したらどうなるかを確認し、nullを含まない形に直すif分岐を足してみる- 著者が複数人いるとき (
String[] authors) のメソッドや、引数なしのBook()で"untitled by unknown"を返すパターンを足してみる
コンストラクタは、ここから先のレッスン (継承、record、final フィールド、Builder パターンなど) でも何度も再登場する主役級の道具です。「new するときにフィールドを埋める専用の関数」というイメージを今のうちに体に染み込ませておきましょう。
よくある質問
Q. コンストラクタを書かないとどうなりますか?
A. Java はデフォルトコンストラクタ(引数なし)が自動生成されます。フィールドを初期化したいなら明示的に書く必要があります。一度でも引数つきコンストラクタを定義するとデフォルトは作られないため、引数なしも欲しいなら両方書きましょう。
Q. コンストラクタを複数定義できますか?
A. Java はオーバーロードで何個でも定義できます。this(...) で別のコンストラクタを呼び出すと共通処理を 1 か所に集約できます。「最小限の引数 + 全部入りバージョン」を組にすると呼び出し側で柔軟に使えます。
Q. コンストラクタで例外を投げて良いですか?
A. 投げても問題ありません。引数バリデーション(負の値を弾く等)で IllegalArgumentException を投げるとフィールドに不正値が入るのを防げます。コンストラクタが例外で抜けるとインスタンスは生成されず参照も返らないので、後始末が要らない点もメリットです。
次のレッスン
次は this キーワード で、this キーワード を学びます。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- コンストラクタ の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. コンストラクタ とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
要件
- クラス名は
Solution、メソッド名はbookFullTitle、引数はString titleとString authorの 2 つにすること - 戻り値の型は
Stringで、title + " by " + authorの形の文字列を返すこと titleとauthorの間に挟む文字列はby(前後に半角スペース 1 つずつ) を厳密に守ること
入出力例
test-cases.txt
bookFullTitle("Java入門", "著者A") → "Java入門 by 著者A"
bookFullTitle("Effective Java", "Bloch") → "Effective Java by Bloch"
bookFullTitle("リーダブルコード", "Boswell") → "リーダブルコード by Boswell"
bookFullTitle("A", "B") → "A by B"
bookFullTitle("", "著者") → " by 著者"