Java

クラスとオブジェクト

関係するデータと操作をまとめ、不正な状態を防げる設計を学びます。

01

クラスを使う場面

商品名と価格、口座名義と残高のように、常に一緒に扱うデータと操作がある場合はクラスにまとめると関係が明確になります。

クラスにする目安
  • 常に一緒に扱う複数の値がある
  • その値へ行う操作が複数ある
  • 外部から勝手に変更されると困るルールがある
02

フィールド、コンストラクタ、メソッド

フィールドが状態、コンストラクタが初期化、メソッドが操作を担当します。

Product.javaJAVA
public class Product {
    private final String name;
    private final int price;

    public Product(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public String label() {
        return name + "(" + price + "円)";
    }

    public static void main(String[] args) {
        Product book = new Product("Java入門", 2000);
        System.out.println(book.label());
    }
}
実行結果OUTPUT
Java入門(2000円)

this.nameはこのインスタンスのフィールド、右辺のnameは引数です。finalにより初期化後の再代入を防ぎます。

03

不正な状態を防ぐ

コンストラクタで検証すれば、負の価格を持つ商品を最初から作れません。

検証を追加JAVA
public Product(String name, int price) {
    if (name == null || name.isBlank()) {
        throw new IllegalArgumentException("商品名は必須です");
    }
    if (price < 0) {
        throw new IllegalArgumentException("価格は0以上です");
    }
    this.name = name;
    this.price = price;
}

フィールドをprivateにし、必要な操作だけをpublicメソッドとして公開する考え方をカプセル化と呼びます。

04

データ中心ならrecord

主に値を保持する型はrecordで簡潔に書けます。アクセサー、equals()hashCode()toString()が生成されます。

User.javaJAVA
public record User(String name, int age) {
    public User {
        if (age < 0) {
            throw new IllegalArgumentException("年齢は0以上です");
        }
    }

    public static void main(String[] args) {
        User user = new User("Mity", 20);
        System.out.println(user.name());
        System.out.println(user);
    }
}
実行結果OUTPUT
Mity
User[name=Mity, age=20]

recordのコンポーネント参照は再代入できません。ただし保持したListなどの中身まで自動で不変になるわけではありません。

05

インターフェースと委譲

インターフェースは「何ができるか」という契約を表します。利用側が具体的な実装に強く依存しない形にできます。

通知の切替JAVA
interface Notifier {
    void send(String message);
}

class ConsoleNotifier implements Notifier {
    public void send(String message) {
        System.out.println("通知: " + message);
    }
}

class OrderService {
    private final Notifier notifier;

    OrderService(Notifier notifier) {
        this.notifier = notifier;
    }

    void complete() {
        notifier.send("注文が完了しました");
    }
}
呼び出しと実行結果JAVA / OUTPUT
new OrderService(new ConsoleNotifier()).complete();
// 通知: 注文が完了しました

継承は「AはBの一種」と言える場合に使います。単に別機能を利用するなら、上のようにフィールドとして受け取る委譲をまず検討します。

PRACTICE

ミニ課題:銀行口座

口座名義と残高を持つBankAccountを作り、預け入れと引き出しをメソッドにします。0以下の金額や残高を超える引き出しは例外にしてください。

確認する順番 初期残高0、1000円を預ける、400円を引き出す、残高600円、700円の引き出しは失敗、という流れをテストします。