8日目のゴールとテーマ
8日目のテーマは
「カプセル化(情報を隠す)と getter / setter で、“壊れにくいクラス”に育てる」 です。
7日目で、あなたはすでに
自分でクラス(Task)を作り
オブジェクトを new して
ArrayList<Task> で複数のタスクを扱い
Task にメソッド(print, complete)という“振る舞い”を持たせる
ところまで来ました。
今日はそこから一歩進んで、
フィールドを外から丸見えにしない
メソッド経由で安全に値を変える
「クラスの中身を守る」という設計の感覚を持つ
ここを押さえていきます。
なぜ「情報を隠す必要」があるのか
フィールドが丸見えだと、どこからでも“いじれてしまう”
昨日の Task クラスは、こんな形でした。
public class Task {
String title;
boolean done;
public Task(String title) {
this.title = title;
this.done = false;
}
public void complete() {
this.done = true;
}
public void printWithNumber(int number) {
String status = done ? "完了" : "未完了";
System.out.println(number + ": " + title + " / 状態: " + status);
}
}
Javaここでの問題は、title と done が「どこからでも直接いじれてしまう」ことです。
例えば、Main 側でこう書けてしまいます。
Task task = new Task("Javaの勉強をする");
task.done = true; // いきなり完了にする
task.title = ""; // タイトルを空にしてしまう
task.done = false; // また未完了に戻す
Javaこれだと、
Task クラスの外側から、
Task の状態が好き放題に変えられてしまう
という状態になります。
小さいプログラムならまだしも、
規模が大きくなると「どこで誰が何を変えたのか」が追えなくなり、
バグの温床になります。
そこで出てくる考え方が、
「カプセル化(情報隠蔽)」 です。
カプセル化の基本アイデア
「中身は隠して、“窓口”だけ公開する」
カプセル化の発想はシンプルです。
フィールド(中身)は外から直接触れないようにする
代わりに、「こういうときだけ変えていい」というメソッドを用意する
つまり、
中身は隠す
操作の仕方だけ公開する
という形にします。
Javaでは、これを「アクセス修飾子」で表現します。
public
どこからでもアクセスできる
private
同じクラスの中からしかアクセスできない
フィールドは private にして、
必要な操作だけ public なメソッドとして公開する。
これがカプセル化の基本パターンです。
Taskクラスをカプセル化してみる
フィールドをprivateにして、外から直接触れないようにする
まずは、フィールドに private を付けます。
public class Task {
private String title;
private boolean done;
public Task(String title) {
this.title = title;
this.done = false;
}
public void complete() {
this.done = true;
}
public void printWithNumber(int number) {
String status = done ? "完了" : "未完了";
System.out.println(number + ": " + title + " / 状態: " + status);
}
}
Javaこれで、Main 側から
task.title = "xxx";
task.done = true;
Javaのように直接いじることはできなくなります。
Task の中からは、title や done にそのままアクセスできますが、
外からは「見えない」「触れない」状態になります。
これが「情報をカプセルの中に閉じ込める」という意味です。
getterとsetterとは何か
「中身を“読む窓”と“変える窓”」
フィールドを private にすると、
外からはその値を読むことも、変えることもできなくなります。
でも、アプリとしては、
タイトルを表示したい
完了状態をチェックしたい
タイトルを変更したい
といった場面が出てきます。
そこで使うのが、
getter(ゲッター) と setter(セッター) です。
getter
フィールドの値を「読み取る」ためのメソッド
setter
フィールドの値を「変更する」ためのメソッド
「中身は隠すけれど、必要なときだけ窓口を通してアクセスさせる」
というイメージです。
Taskにgetterを追加する
「値を読むだけ」のメソッド
まずは、「読むだけ」の窓を作ってみます。
public class Task {
private String title;
private boolean done;
public Task(String title) {
this.title = title;
this.done = false;
}
public String getTitle() {
return this.title;
}
public boolean isDone() {
return this.done;
}
public void complete() {
this.done = true;
}
public void printWithNumber(int number) {
String status = done ? "完了" : "未完了";
System.out.println(number + ": " + title + " / 状態: " + status);
}
}
Javaここでのポイントを深掘りします。
public String getTitle()
タイトルを返す getter です。
慣習として、「get + フィールド名(先頭大文字)」という名前にします。
public boolean isDone()
boolean 型のフィールドの場合、
「is + 名前」という形の getter をよく使います。getDone() でも動きますが、isDone() のほうが自然です。
Main 側では、こう使えます。
Task task = new Task("Javaの勉強をする");
System.out.println(task.getTitle());
System.out.println(task.isDone());
Javaフィールドは private で隠しつつ、
「読むための窓口」だけを公開している状態です。
Taskにsetterを追加する
「変えていいルールをメソッドの中に閉じ込める」
次に、「値を変更する窓」を作ってみます。
public class Task {
private String title;
private boolean done;
public Task(String title) {
setTitle(title);
this.done = false;
}
public String getTitle() {
return this.title;
}
public boolean isDone() {
return this.done;
}
public void setTitle(String title) {
if (title == null || title.isEmpty()) {
System.out.println("タイトルが空です。変更を無視します。");
return;
}
this.title = title;
}
public void complete() {
this.done = true;
}
public void printWithNumber(int number) {
String status = done ? "完了" : "未完了";
System.out.println(number + ": " + title + " / 状態: " + status);
}
}
Javaここでの重要ポイントは setTitle です。
public void setTitle(String title)
タイトルを変更するための setter です。
慣習として、「set + フィールド名(先頭大文字)」という名前にします。
if (title == null || title.isEmpty()) { ... }
ここで「タイトルは空文字や null は許さない」というルールを入れています。
もし空なら、警告を出して変更を無視します。
コンストラクタの中でも setTitle(title); を使っています。
これにより、「作るとき」も「あとから変えるとき」も、
同じルールが適用されます。
Main 側では、こう書けます。
Task task = new Task("Javaの勉強をする");
task.setTitle("Javaの復習をする"); // OK
task.setTitle(""); // 無視される(ルール違反)
Java「どういう値なら許すか」というルールを、
Task クラスの中に閉じ込めているのがポイントです。
カプセル化の本質
「クラスの外から“勝手に壊されない”ようにする」
ここまでをまとめると、カプセル化の本質はこうです。
フィールドを private にして、
外から直接いじれないようにする。
代わりに、
getter / setter や、意味のあるメソッド(complete など)を通してだけ、
状態を読んだり変えたりできるようにする。
そのときに、
「こういう値はダメ」「こういうときだけ変えていい」
というルールをメソッドの中に書いておく。
これによって、
クラスの外から、
そのクラスの“前提条件”を壊されにくくなる
という効果が生まれます。
クラス版ToDoアプリを「カプセル化対応」にする
Main側のコードがどう変わるかを見る
7日目のクラス版ToDoアプリを、
カプセル化された Task に合わせて少しだけ見直します。
Task クラスは、さっきの getter / setter 付きのものを使う前提です。
Main 側の一部を抜き出して見てみます。
public static void printTasks(ArrayList<Task> tasks) {
if (tasks.isEmpty()) {
System.out.println("タスクは登録されていません。");
} else {
System.out.println("=== タスク一覧 ===");
for (int i = 0; i < tasks.size(); i++) {
Task task = tasks.get(i);
String status = task.isDone() ? "完了" : "未完了";
System.out.println((i + 1) + ": " + task.getTitle() + " / 状態: " + status);
}
}
}
Javaここでは、
タイトルは task.getTitle() で取得
完了状態は task.isDone() で取得
しています。
もし Task の中で「状態の表現」を変えたくなっても、
Main 側は getter を呼んでいるだけなので、影響を最小限にできます。
完了処理もこうです。
public static void completeTask(Scanner scanner, ArrayList<Task> tasks) {
if (tasks.isEmpty()) {
System.out.println("完了にできるタスクがありません。");
return;
}
System.out.println("完了にするタスクの番号を入力してください:");
for (int i = 0; i < tasks.size(); i++) {
Task task = tasks.get(i);
String status = task.isDone() ? "完了" : "未完了";
System.out.println((i + 1) + ": " + task.getTitle() + " / 状態: " + status);
}
System.out.print("番号: ");
int index = scanner.nextInt();
scanner.nextLine();
int realIndex = index - 1;
if (realIndex < 0 || realIndex >= tasks.size()) {
System.out.println("その番号のタスクは存在しません。");
} else {
Task task = tasks.get(realIndex);
task.complete();
System.out.println("タスクを完了にしました。");
}
}
Java完了にするときも、task.done = true; ではなく task.complete(); を呼んでいます。
「Task の状態をどう変えるか」は Task の中に閉じ込めておき、
Main は「完了にして」と命令するだけ。
この分担が、クラス設計の大事な感覚です。
「フィールドpublicでよくない?」にちゃんと答える
最初は楽、あとで地獄
初心者が必ず一度は思う疑問があります。
「最初から public String title; でよくない?
いちいち getter / setter 書くの面倒じゃない?」
これは、短期的には「そうしたくなる気持ち」がよく分かります。
実際、小さなサンプルコードならそれでも動きます。
でも、アプリが少し大きくなると、
どこからでも title を変えられる
どこからでも done を true / false にできる
「どこで壊されたのか」が追えない
という状態になります。
getter / setter を通すことで、
「タイトルは空文字禁止」
「完了状態は complete メソッド経由でしか変えない」
といったルールを、クラスの中に閉じ込められます。
つまり、
最初に少しだけ手間をかけておくことで、
あとからのバグと混乱を大幅に減らせる
という投資になります。
8日目で一番大事な感覚
「クラスの中身は“守るもの”であって、“さらすもの”ではない」
今日あなたに持ってほしい感覚はこれです。
クラスのフィールドは、
「外から好き放題いじっていいもの」ではなく、
「クラス自身が責任を持って管理するもの」です。
外からは、
「この情報を教えて」→ getter
「こういうふうに変えて」→ setter や意味のあるメソッド
という“お願い”だけをする。
中身の守り方(ルール)は、
クラスの中に閉じ込めておく。
この感覚が身につくと、
あなたの書くクラスは一気に「壊れにくく」「育てやすく」なります。
8日目のまとめと、9日目への予告
今日やったことを短く整理すると、
フィールドを private にして、外から直接触れないようにする
getter で「読む窓」、setter で「変える窓」を用意する
setter の中に「この値はダメ」というルールを書ける
complete のような「意味のある操作メソッド」で状態を変える
カプセル化された Task を使って、ToDo アプリをより安全にできる
9日目は、この「クラス設計」の感覚をさらに広げて、
複数のクラス同士をどう関係させるか(「〜が〜を持つ」「〜が〜を使う」)
という視点に入っていきます。
そこまで行くと、現実のアプリにかなり近い構造を、自分の手で組めるようになっていきます。

