Java | 2週間で身につく、アプリを作りながら学ぶJavaの基本 - 7日目

Java Java
スポンサーリンク

7日目のゴールとテーマ

7日目のテーマは
「クラスとオブジェクトで“自分だけの型”を作り、アプリの世界に登場人物を生み出す」 です。

ここまでで、あなたは
変数・配列・ArrayListでデータを扱い、
if・for・whileで処理の流れを作り、
メソッドで処理を分けて整理する、
というところまで来ました。

今日は、Javaの中核である
「クラス」と「オブジェクト」
を使って、
「タスク」「ユーザー」「商品」などを“自分の型”として表現していきます。


クラスとオブジェクトのイメージ

設計図(クラス)と実物(オブジェクト)

まずはイメージから固めます。

クラスは「設計図」です。
「この種類のものは、どんな情報を持っていて、どんな振る舞いをするか」を定義します。

オブジェクトは「設計図から作られた実物」です。
Task という設計図があれば、
「Javaの勉強をする」というタスクも、
「買い物に行く」というタスクも、
どちらも Task という型の“実物”として扱えます。

同じクラスから、何個でもオブジェクトを作れます。
これが「クラスとオブジェクト」の基本的な関係です。


最初のクラスを作ってみる

Taskクラスの骨組みを定義する

ToDoアプリで使う「タスク」を、クラスとして表現してみましょう。

public class Task {
    String title;
    boolean done;
}
Java

ここでやっていることをかみ砕きます。

public class Task { ... }
これは「Task という名前のクラスを定義します」という宣言です。
ファイル名は Task.java にするのが基本です。

String title;
これは「タスクのタイトル」を表すフィールド(メンバ変数)です。

boolean done;
これは「完了しているかどうか」を表すフィールドです。
true なら完了、false なら未完了という意味にします。

この時点では、Task は「設計図だけ」で、実物はまだありません。


オブジェクトを作って使う

newで“実物”を作る

Task クラスを使うには、別のクラス(Main など)からオブジェクトを作ります。

public class Main {
    public static void main(String[] args) {
        Task task1 = new Task();
        task1.title = "Javaの勉強をする";
        task1.done = false;

        System.out.println("タイトル: " + task1.title);
        System.out.println("完了?: " + task1.done);
    }
}
Java

流れを丁寧に追ってみます。

Task task1 = new Task();
Task 型の変数 task1 を宣言し、new Task() で Task のオブジェクトを1つ作っています。
new は「設計図から実物を作る」キーワードです。

task1.title = "Javaの勉強をする";
作ったオブジェクト task1 の title フィールドに、文字列を代入しています。

task1.done = false;
done フィールドに false を入れています。
これで「未完了のタスク」という状態になります。

クラスを作る → new でオブジェクトを作る → フィールドに値を入れる。
この流れが、クラスとオブジェクトの基本です。


複数のオブジェクトを扱う

ArrayListと組み合わせて“タスク一覧”を持つ

Task を1つだけでなく、複数扱ってみましょう。
ArrayList と組み合わせると、一気にアプリらしくなります。

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        ArrayList<Task> tasks = new ArrayList<>();

        Task task1 = new Task();
        task1.title = "Javaの勉強をする";
        task1.done = false;

        Task task2 = new Task();
        task2.title = "買い物に行く";
        task2.done = true;

        tasks.add(task1);
        tasks.add(task2);

        for (Task task : tasks) {
            System.out.println("タイトル: " + task.title + " / 完了?: " + task.done);
        }
    }
}
Java

ここでの重要ポイントは2つです。

ArrayList<Task>
自分で作った Task クラスも、型として ArrayList に入れられます。
ArrayList<String>ArrayList<Integer> と同じ感覚で使えます。

for (Task task : tasks)
拡張for文で、tasks の中の Task オブジェクトを1つずつ取り出しています。
task はループの中で「今見ているタスク1件」を表します。

「自分で作った型を、リストで管理する」
これができると、アプリの世界に“登場人物”を増やしていけるようになります。


コンストラクタで初期化をまとめる

newした瞬間の“セットアップ”を1か所に集める

さっきの Task 作成コードは、こうでした。

Task task1 = new Task();
task1.title = "Javaの勉強をする";
task1.done = false;
Java

毎回 title と done をセットするのは、少し面倒です。
そこで使うのが「コンストラクタ」です。

Task クラスを、こう書き換えます。

public class Task {
    String title;
    boolean done;

    public Task(String title) {
        this.title = title;
        this.done = false;
    }
}
Java

ここでのポイントを深掘りします。

public Task(String title)
クラス名と同じ名前の“特別なメソッド”がコンストラクタです。
new したときに自動的に呼ばれます。

this.title = title;
左側の this.title は「このオブジェクトの title フィールド」です。
右側の title は、コンストラクタの引数として受け取った値です。
「渡されたタイトルを、このオブジェクトの title にセットする」という意味になります。

this.done = false;
作られた瞬間は、必ず未完了にしておく、というルールをここに書いています。

これで、Main 側はこう書けます。

Task task1 = new Task("Javaの勉強をする");
Task task2 = new Task("買い物に行く");
Java

「作るときに必要な情報」をコンストラクタにまとめることで、
オブジェクトの初期化がスッキリし、間違いも減ります。


クラスに“振る舞い”を持たせる

Task自身に「自分の表示の仕方」を知ってもらう

今は、Task の情報を表示するとき、Main 側でこう書いています。

System.out.println("タイトル: " + task.title + " / 完了?: " + task.done);
Java

これを Task クラスの中に、「自分の表示の仕方」として持たせてみましょう。

public class Task {
    String title;
    boolean done;

    public Task(String title) {
        this.title = title;
        this.done = false;
    }

    public void print() {
        String status = done ? "完了" : "未完了";
        System.out.println("タイトル: " + title + " / 状態: " + status);
    }
}
Java

ここでのポイントは、print メソッドです。

public void print()
戻り値なし(void)のメソッドで、Task オブジェクト自身の情報を表示します。

String status = done ? "完了" : "未完了";
これは三項演算子です。
条件 ? 真のときの値 : 偽のときの値 という形で書きます。
done が true なら “完了”、false なら “未完了” になります。

Main 側は、こう書けるようになります。

for (Task task : tasks) {
    task.print();
}
Java

「Task の表示の仕方」は Task クラスの中に閉じ込めておき、
Main は「print して」と頼むだけ。
この分担がとても大事です。


状態を変えるメソッドを作る

「Taskに“自分を完了にする”操作を持たせる」

次に、「タスクを完了にする」操作を 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 print() {
        String status = done ? "完了" : "未完了";
        System.out.println("タイトル: " + title + " / 状態: " + status);
    }
}
Java

public void complete()
このメソッドは、「このタスクを完了状態にする」という意味を持ちます。
中身は this.done = true; だけですが、
「complete という名前で呼べる」ということがとても大事です。

Main 側では、こう使えます。

Task task = new Task("Javaの勉強をする");
task.print();      // 未完了と表示される
task.complete();   // 完了にする
task.print();      // 完了と表示される
Java

「フィールドを直接いじる」のではなく、
「意味のあるメソッドを通して状態を変える」ことで、
コードの意図が読みやすくなります。


クラスを使ったToDoアプリの形を考える

「Taskのリスト」を扱うアプリに育てる

ここまでの要素を組み合わせて、
5〜6日目の ToDo アプリを「Task クラス版」に進化させてみましょう。

やりたいことはこうです。

Task クラスで「1件のタスク」を表す
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

printWithNumber は、「番号付きで表示する」ためのメソッドです。
番号は外から渡してもらいます。


クラス版ToDoアプリのメイン部分

MainクラスでTaskのリストを操作する

次に、Main クラス側です。

import java.util.ArrayList;
import java.util.Scanner;

public class Main {

    public static void printMenu() {
        System.out.println("=== クラス版ToDoリスト ===");
        System.out.println("1: タスクを追加する");
        System.out.println("2: タスク一覧を表示する");
        System.out.println("3: タスクを完了にする");
        System.out.println("0: 終了する");
        System.out.print("番号を選んでください: ");
    }

    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);
                task.printWithNumber(i + 1);
            }
        }
    }

    public static void addTask(Scanner scanner, ArrayList<Task> tasks) {
        System.out.print("追加するタスクを入力してください: ");
        String title = scanner.nextLine();
        if (title.isEmpty()) {
            System.out.println("空のタスクは追加できません。");
        } else {
            Task task = new Task(title);
            tasks.add(task);
            System.out.println("タスクを追加しました。");
        }
    }

    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);
            task.printWithNumber(i + 1);
        }

        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("タスクを完了にしました。");
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        ArrayList<Task> tasks = new ArrayList<>();

        while (true) {
            printMenu();

            int choice = scanner.nextInt();
            scanner.nextLine();  // 改行を読み飛ばす

            if (choice == 0) {
                System.out.println("アプリを終了します。");
                break;
            } else if (choice == 1) {
                addTask(scanner, tasks);
            } else if (choice == 2) {
                printTasks(tasks);
            } else if (choice == 3) {
                completeTask(scanner, tasks);
            } else {
                System.out.println("その番号は無効です。");
            }

            System.out.println();
        }
    }
}
Java

ここでの重要ポイントを深掘りします。

ArrayList<Task> tasks
タスク1件1件を Task オブジェクトとして扱い、
それをリストでまとめています。
「タスクの情報が増えても、Task クラスにフィールドを足せばよい」という状態になります。

task.printWithNumber(i + 1);
表示の仕方は Task クラスに任せています。
Main は「何番目のタスクか」だけ渡して、「表示して」と頼んでいるだけです。

task.complete();
完了状態の変更も Task クラスに任せています。
Main は「このタスクを完了にして」と命令するだけです。

「データ」と「そのデータに対する操作」を、
1つのクラスにまとめている、というのがポイントです。


クラスを使う意味を整理する

「ただのデータの集まり」から「意味のある“もの”へ」

クラスを使わずにタスクを表現しようとすると、
タイトルの配列、完了フラグの配列、などを別々に持つことになります。

ArrayList<String> titles;
ArrayList<Boolean> dones;
Java

この形だと、

タイトルと完了状態がバラバラ
インデックスで対応づける必要がある
間違えてズレるとバグになる

という不安定な状態になります。

一方、Task クラスを使うと、

1つの Task オブジェクトが「タイトル」と「完了状態」をセットで持つ
Task に「表示する」「完了にする」といった振る舞いを持たせられる
Task のリストとして扱える

という、自然な形になります。

クラスを使うというのは、
「ただのデータの集まり」を
「意味のある“もの”として扱う」
ということです。


7日目で一番大事な感覚

「クラスを作ると、アプリの世界に“登場人物”が生まれる」

今日あなたに持ってほしい感覚はこれです。

Task クラスを作った瞬間、
あなたのアプリの世界に「タスク」という登場人物が生まれました。

そのタスクは、
タイトルという情報を持ち、
完了か未完了かという状態を持ち、
自分の情報を表示することができ、
自分を完了にすることもできます。

クラスを作るとは、
アプリの中に「概念」を1つ生み出すことです。
そして、その概念を“型”として扱えるようにすることです。


7日目のまとめと、8日目への予告

今日やったことを短く整理すると、

クラスは「設計図」、オブジェクトは「実物」
Task クラスで「1件のタスク」を表現した
コンストラクタで「作るときの初期化」をまとめた
Task に print や complete といった“振る舞い”を持たせた
ArrayList<Task> で「タスク一覧」を扱うアプリを書いた

8日目は、この「クラスとオブジェクト」の考え方をもう少し深めて、
カプセル化(情報を隠す)、getter / setter、
そして「クラスをどう設計するか」という視点に踏み込んでいきます。
そこまで行くと、Javaで「ちゃんとしたアプリ」を作るための土台がかなり整ってきます。

タイトルとURLをコピーしました