Person クラスを作る
Person クラスを作る とは
Person クラスを設計しながら、フィールド・コンストラクタ・メソッドの基本を身につけよう。インスタンスから自己紹介の文字列を取り出す課題に挑戦する。
クラスは「設計図」、インスタンスは「実体」
ここまでのレッスンでは、int や String のような すでに用意されている型 を組み合わせてプログラムを書いてきました。けれど現実のソフトウェアでは、「ユーザー」「商品」「予約」「投稿」のように、Java 標準にはない アプリ独自の概念 を表現したくなります。そんなとき登場するのが class です。クラスは「こんな情報とこんな振る舞いを持ったモノを作ります」という 設計図 で、その設計図から実際に作られた Java 上の物体を インスタンス (またはオブジェクト) と呼びます。
たい焼きの金型を思い浮かべてください。金型そのものは食べられない「設計図」、そこから焼き上がった一つひとつのたい焼きが「インスタンス」です。同じ金型から
太郎のたい焼きも花子のたい焼きも焼けますが、それぞれは別の存在で、誰かが食べても他のたい焼きはそのまま残ります。
このレッスンでは、人を表す Person クラスを設計しながら、Solution.personIntroduction(String name, int age) という自己紹介の文字列を返すメソッドを完成させます。例として personIntroduction("太郎", 25) を呼ぶと "太郎 (25)" という文字列が返ってきます。たった 1 行の課題に見えますが、その裏には Java のオブジェクト指向のエッセンスが詰まっています。
Person クラスの最小形
まずは Person クラスの最小形を見てみましょう。name (名前) と age (年齢) という 2 つの情報を持つ人物を表現します。
Java
public class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
String introduce() {
return this.name + " (" + this.age + ")";
}
}短いコードですが、Java のクラスを構成する 3 大要素である フィールド コンストラクタ メソッド がきれいに 1 つずつ入っています。順に読み解いていきましょう。
String name;とint age;— フィールド (field)。クラスが内部で持っているデータ。インスタンスごとに別の値を持てるPerson(String name, int age) { ... }— コンストラクタ (constructor)。new Person("太郎", 25)のように呼んで、インスタンスを 生まれた瞬間 に初期化するための特別なメソッドString introduce()— メソッド (method)。インスタンスに「やってもらう振る舞い」を書く場所
注目したいのは、コンストラクタとメソッドの中に出てくる this という言葉です。this.name の this は「いま動いているインスタンス自身」を指す Java の予約語で、引数の name と フィールドの name が同じ名前でぶつかったときに どちらの値を指したいかを区別するための合図 として使います。
this.name = name;は呪文ではなく、「いま動いている自分 (this) のnameフィールドに、引数で受け取ったnameの値を入れる」という代入文です。左辺は フィールド 、右辺は 引数 、と頭の中で読み分けてください。慣れないうちは「内側 (this.name) に外側 (name) を入れる」と覚えると間違えにくくなります。
インスタンスを 2 つ作って独立性を体感する
クラスの威力は、同じ設計図から複数のインスタンスを作っても、それぞれが独立した状態を持てる ところにあります。試しに Person を 2 人ぶん作ってみましょう。
Java
Person taro = new Person("太郎", 25);
Person hanako = new Person("花子", 30);
String s1 = taro.introduce(); // "太郎 (25)"
String s2 = hanako.introduce(); // "花子 (30)"taro と hanako は同じ Person クラスから作られていますが、taro.name は "太郎"、hanako.name は "花子" で、まったく別のデータを保持しています。たい焼きの例えに戻ると、同じ金型から 太郎 と 花子 の 2 匹のたい焼きが焼かれて、皿の上に並んでいる状態です。taro を食べても hanako には影響しません。
newキーワードはクラスからインスタンスを生み出すための合言葉です。new Person("太郎", 25)と書くとJavaは内部で「Personクラスのインスタンス用にメモリ領域を確保 → コンストラクタを実行 → そのインスタンスへの参照を返す」という流れを実行してくれます。コンストラクタを呼ぶときは必ずnewとセットだと覚えてください。
ここで、Person クラスと 2 つのインスタンスの関係を図にしておきます。設計図 (クラス) は 1 つ、そこから生まれたインスタンスは独立した別物、という関係をつかんでください。
クラスは抽象的な「型」、インスタンスはメモリの上に実在する「モノ」です。Person が消えてもインスタンスは消えませんし、逆も同じです。
課題で書く Solution.personIntroduction の中身
このレッスンの課題では、Solution クラスの中に static メソッド として personIntroduction(String name, int age) を実装します。中身は実質、Person クラスを使った自己紹介と同じです。一番素直な書き方は、Person インスタンスを作って introduce() を呼ぶ流れをそのまま再現することです。
Java
public class Solution {
public static String personIntroduction(String name, int age) {
Person p = new Person(name, age);
return p.introduce();
}
static class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
String introduce() {
return this.name + " (" + this.age + ")";
}
}
}personIntroduction("太郎", 25) を呼ぶと、内部で new Person("太郎", 25) が走ってインスタンスが生まれ、その introduce() が "太郎 (25)" を返してくれます。もちろん 1 行で return name + " (" + age + ")"; と書いても同じ結果になりますが、本レッスンの主役は クラスを実際に作って使う体験 です。テストが通る範囲で、ぜひクラスを定義して呼び出す形で書いてみてください。
static class Personのstaticは、ここでは「外側のSolutionクラスに紐づく内側クラス」を意味する書き方です。Javaの入れ子クラス (nested class) と呼ばれます。本来Person.javaという別ファイルに分けるのが普通ですが、chotdekiru の Java Playground は単一ファイル前提なので、Solutionの中にstatic classとして書く形を採用しています。
クラスとデータ構造の使い分け
「String 配列で [name, age] を表せばよくない?」と思った人は鋭いです。Java でも new String[] { "太郎", "25" } のように 2 要素の配列で持つことはできます。けれど、配列や Map のような汎用データ構造には次のような弱点があります。
- 各要素が 何を表しているか がコードからは読み取れない (
arr[0]が名前なのか年齢なのかが分からない) - 値の 型がそろってしまう ので、
Stringとintを混ぜたいときに不便 (Object[]にすると型安全性を失う) - 「自己紹介する」のような振る舞いを データと一緒に持つ ことができない
クラスを使うと、これらの弱点が一気に解消されます。Person p の p.name p.age p.introduce() は、フィールドもメソッドも自分自身の中に閉じていて、コードを読んだだけで「これは人を表すモノだな」と分かります。
Javaの世界には「データだけならクラスにしなくていい場面」もあります。たとえば短期的に値を一緒に運びたいだけなら、Java 16で導入されたrecord Person(String name, int age) {}という 1 行で済む書き方の方が向いています。recordはフィールド・コンストラクタ・equals・hashCodeを自動生成してくれる、いわば「データを運ぶ用」の軽量クラスです。今回はclassの基本を学ぶのが目的なので普通のclassを使いますが、現場ではclassとrecordを場面で使い分けます。
よくある間違い
初めてクラスを書くときに、ほとんどの人が一度はやらかすミスがあります。代表的な 3 つを押さえておきましょう。
- フィールドの初期化漏れ — フィールドだけ宣言して、コンストラクタの中で
this.name = name;を書き忘れるパターン。コンパイルは通ってしまうのに、introduce()を呼ぶとnameがnullのままで"null (0)"のような結果が返ってきます。JavaではStringのフィールドは初期値null、intのフィールドは初期値0になる仕様なので、入れ忘れに気づきにくいのが厄介です - コンストラクタなしで放置する — コンストラクタを 1 つも書かないと、
Javaが自動で 引数なしのデフォルトコンストラクタ を作ってしまいます。するとnew Person("太郎", 25)という書き方ができず、コンパイラからconstructor Person in class Person cannot be applied to given typesと怒られます。引数付きで初期化したいなら、その引数を取るコンストラクタを必ず自分で書く のが鉄則です equalsを実装していないのに==で比較する — 2 人のPersonを比べたくてtaro == hanakoのように==で書くと、Javaは メモリ上の同じ場所を指しているか だけを見ます。中身が同じでもnewで別々に作っていれば結果はfalseです。中身を比べたいときはequalsメソッドを自分で実装する必要があります
もうひとつ、地味ですが多いのが フィールドとローカル変数の混同 です。コンストラクタの中で this. を付け忘れて name = name; と書くと、引数の name を引数自身に代入しているだけで、フィールドの値は更新されません。Java のコンパイラは警告を出してくれることもありますが、確実にしたいなら this. を付ける ことを習慣にしてください。
エラーが起きたときの読み方のコツは、まず
cannot find symbolcannot be applied to given typesNullPointerExceptionのような エラー名 を拾うことです。Javaのエラーは長く見えますが、頭の単語だけ拾えば 8 割は原因の手がかりになります。
やってみよう
それでは右側のエディタで、Solution.personIntroduction(String name, int age) メソッドを完成させましょう。手順は次の通りです。
- メソッドのカッコの中が
String name, int ageの 2 つの引数になっていることを確認する Personインスタンスをnew Person(name, age)で作る- そのインスタンスの
introduce()を呼んで、戻り値をそのままreturnする - 「実行」ボタンを押して、すべてのテストが緑になることを確認する
動いたら、ぜひ自由に実験してみてください。たとえば new Person(name, age) を 2 つ作って、それぞれの introduce() が独立した結果を返すことを確認してみたり、Person クラスに String greet() のような別のメソッドを追加してみたり、age の代わりに int birthYear を持たせて年齢を計算で求めるバージョンを書いてみたり、と発展課題はいくらでも作れます。
クラスを書けるようになると、Java で表現できる世界は一気に広がります。ユーザー、商品、注文、メッセージ、ゲームの登場人物 ─ どれも class の形でモデル化することで、データと振る舞いをひとつの塊として扱えるようになります。次のレッスン以降では、この Person クラスを土台に、クラス同士の関係や継承、record といった発展トピックに進んでいきます。今は「フィールド・コンストラクタ・メソッド」の 3 点セットを、自分の指でしっかり打ち込んで身体に覚えさせてください。
よくある質問
Q. クラスと関数はどう使い分けますか?
A. 状態(フィールド)を持たせて関連する処理をまとめたいときはクラス、純粋な計算だけなら関数で十分です。Java のように関数を単独で持てない言語ではクラス必須ですが、JS/Python はモジュール関数だけでも十分です。OOP の継承を活かすかどうかで判断しましょう。
Q. private と public の使い分けは?
A. フィールドは原則 private にして外から触らせず、getter / setter 経由でアクセスさせるのがカプセル化の基本です。public は呼び出し元に公開する API、private は内部実装の隠蔽。後から内部を変えても利用側が壊れないメリットがあります。
Q. 1 ファイルに複数クラスを書けますか?
A. Java は 1 つの public クラスにつき同じファイル名が必要です。public でない補助クラスは同じファイルに置けます。JavaScript/Python は何個でも置けますが、再利用性のために 1 ファイル 1 クラスを基本にした方が読み手に優しいです。
次のレッスン
次は Product クラスで税込価格 で、Person クラスを設計しながら、フィールド・コンストラクタ・メソッドの基本を身につけよう を学びます。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- Person の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. Person とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
要件
- クラス名は
Solution、メソッド名はpersonIntroduction、引数はString nameとint ageの 2 つにすること - メソッドの戻り値の型は
Stringで、"name (age)"の形式 (名前のあとに半角スペース、(、年齢、)を続ける) で返すこと Person内部クラスを定義し、new Person(name, age)でインスタンスを作ってintroduce()を呼び出す流れで実装すること
入出力例
test-cases.txt
personIntroduction("太郎", 25) → "太郎 (25)"
personIntroduction("花子", 30) → "花子 (30)"
personIntroduction("Alice", 18) → "Alice (18)"
personIntroduction("赤ちゃん", 0) → "赤ちゃん (0)"
personIntroduction("長寿さん", 100) → "長寿さん (100)"