SJ blog
beginner
S

信頼度ランク

S 公式ソース確認済み
A 成功実績多数・失敗例少数
B 賛否両論
C 動作未確認・セキュリティリスク高
Z 個人所感

アクセス修飾子はなぜあるのか — カプセル化という「壁を作る」設計思想

public/private/protected/package-privateの意味と使い分け。なぜprivateにするのか、getterとsetterは何のためにあるのかを根本の思想から解説する。

一言結論

アクセス修飾子はカプセル化のための道具。privateで「内部実装」と「外部インターフェース」を分離することで、内部の変更が外部に波及しない設計を実現する。

なぜ「見える範囲」を制限するのか

Javaには4種類のアクセス修飾子がある:

public class BankAccount {
    public    String owner;    // どこからでも見える
    protected double balance;  // 同パッケージ + 子クラスから見える
    double    limit;           // 同パッケージからのみ見える(package-private)
    private   String pin;      // このクラスの中からのみ見える
}

「全部publicにすれば便利なのでは?」と思うかもしれない。実際、初心者のコードは全部publicになりがちだ。でも全部公開すると何が起きるのか。

全部publicにしたとき何が起きるか

public class BankAccount {
    public double balance;  // 残高を公開してしまった
}

// 利用者側(悪意なくても起きる)
BankAccount account = new BankAccount();
account.balance = -99999;  // 残高をマイナスにできてしまう

残高は通常マイナスにできないはずだ。でもbalancepublicなら、誰でも直接書き換えられる。銀行口座の「内部ルール」を無視した操作が可能になってしまう。

さらに困るのは、内部実装を変えられなくなることだ:

// 最初の実装
public class Temperature {
    public double celsius;  // 摂氏で管理していた
}

// 利用者が直接フィールドを使う
Temperature t = new Temperature();
t.celsius = 100.0;

後になって「内部はケルビンで管理したい」と変えようとしても:

public class Temperature {
    public double kelvin;  // 変更!
    // celsius は削除した
}

t.celsiusを使っているコードが全部壊れる。内部実装を変えると外部が壊れる、という事態になる。

カプセル化:「見せるもの」と「隠すもの」を分ける

カプセル化とは「内部の実装を隠し、外部から安全にアクセスできる窓口だけを公開する」設計思想だ。

public class BankAccount {
    private double balance;  // 内部実装(隠す)

    // 公開する窓口
    public void deposit(double amount) {
        if (amount <= 0) throw new IllegalArgumentException("金額は正の数で");
        balance += amount;
    }

    public void withdraw(double amount) {
        if (amount <= 0) throw new IllegalArgumentException("金額は正の数で");
        if (amount > balance) throw new IllegalStateException("残高不足");
        balance -= amount;
    }

    public double getBalance() {
        return balance;
    }
}

これで:

BankAccount account = new BankAccount();
account.balance = -99999;  // ❌ エラー(privateなので直接アクセス不可)
account.deposit(-100);     // ❌ 例外がスローされる(検証が入っている)
account.deposit(1000);     // ✅ 正常
account.withdraw(500);     // ✅ 正常
account.withdraw(10000);   // ❌ 例外(残高不足)

「不正な状態のオブジェクト」を作れなくなった。これがカプセル化の恩恵だ。

getterとsetterはなぜあるのか

privateなフィールドに読み書きするためのgetXxx()setXxx()がよく書かれる:

public class Person {
    private String name;
    private int age;

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
}

「それなら最初からpublicにすれば同じでは?」という疑問は正しい感覚だ。単純なgetterとsetterなら確かにpublicフィールドと変わらない。

getterとsetterの本当の価値は後から中に処理を追加できる点にある:

public void setAge(int age) {
    if (age < 0 || age > 150) {
        throw new IllegalArgumentException("年齢が不正です: " + age);
    }
    this.age = age;
}

public String getName() {
    return name != null ? name : "名無し";  // nullの場合のデフォルト値
}

最初は素通しで書いておいて、後からバリデーションやロジックを追加できる。publicフィールドにしてしまうと、後から追加できない。

また、読み取り専用にもできる:

public double getBalance() { return balance; }
// setBalance()は公開しない → 外から書き換えられない

4種類のアクセス修飾子の具体的な範囲

                     同クラス  同パッケージ  子クラス  どこでも
private            |    ✅   |            |         |
(修飾子なし)       |    ✅   |     ✅     |         |
protected          |    ✅   |     ✅     |   ✅    |
public             |    ✅   |     ✅     |   ✅    |    ✅

package-private(修飾子なし) は「同じパッケージ内のクラスからは見える」。パッケージは関連するクラスをまとめる単位なので、「パッケージ内で共有するが外には見せない」という細かい制御ができる。

protected は「子クラスには見せるが外部には隠す」。継承して使うことを前提としたクラスで使う。

実際の使い分け

フィールドは基本的に private
  → 直接アクセスされると内部ルールを破られる可能性がある

メソッドは目的で判断
  → 外部から使ってほしいメソッド: public
  → 内部の補助処理(外部から呼ばれる必要がない): private
  → 子クラスにカスタマイズさせたい: protected

クラスは基本的に public
  → ただし「このパッケージ内でだけ使う補助クラス」はpackage-private

まとめ

アクセス修飾子の目的    = カプセル化(内部実装と外部インターフェースの分離)
privateを使う理由     = 「不正な状態のオブジェクト」を作らせない + 内部実装を変更できるようにする
getterとsetterの価値  = 後からバリデーションやロジックを追加できる柔軟性
全部publicにする問題  = 内部実装を変えると外部が壊れる・不正な操作ができてしまう

private   = このクラスだけ
(なし)   = 同パッケージだけ
protected = 同パッケージ + 子クラス
public    = どこからでも

「privateにしておけとりあえず安全」は正しい。でも本質は「何を外部に公開するかを意識的に選ぶ」ことだ。壁を作ることで、内部をどう変えても外部に影響が及ばない堅牢な設計が生まれる。