可視性を最小にするとは何か
「可視性を最小にする」は、
クラス・フィールド・メソッドのアクセス範囲(public / protected / なし / private)を
「必要な場所にだけ見えるよう、できるだけ狭くする」ことです。
Effective Java でも「クラスとメンバーのアクセス可能性は常に最小限にせよ」と強く推奨されています。
公開すればするほど、そのメンバーは「外から依存される API」になり、後から変えにくくなります。
まずは全部 private にしてみて、本当に必要になったものだけ、
package-private → protected → public の順に、最低限広げる癖をつけると安全です。
Java の可視性(アクセス修飾子)のざっくり整理
4つの可視性
Java の可視性は大きく 4 種類あります。
public
どこからでも見える(プロジェクト全体からアクセス可能)
protected
同じパッケージ+そのクラスを継承したサブクラスから見える
(修飾子なし:package-private)
同じパッケージ内からだけ見える
private
同じクラスの中からしか見えない
カプセル化(情報隠蔽)は、
「内部の実装を外から直接触れないようにし、必要な“入り口”だけ公開する」考え方です。
可視性を最小にするのは、カプセル化を守るための具体的な実践テクニックです。
典型的な悪い例:何でもかんでも public
public フィールドの危険さ
悪い例から見てみます。
public class BankAccount {
public double balance; // NG: 直接読み書きできてしまう
public void deposit(double amount) {
balance += amount;
}
public void withdraw(double amount) {
balance -= amount;
}
}
Javabalance が public なので、どこからでもこう書けてしまいます。
BankAccount acc = new BankAccount();
acc.balance = -1_000_000; // マイナスや異常な値をいきなり代入できる
Javaこれだと、
残高は 0 以上である
不正なマイナス残高は存在しない
といった「口座としての不変条件」を守れません。
本来、残高は deposit / withdraw のようなルール付きメソッドだけで変えるべきで、
balance フィールド自体は外から直接触れないようにすべきです。
良い例:まず private にして、必要な入り口だけ公開する(重要)
フィールドを隠し、メソッドで操作させる
先ほどの BankAccount を修正してみます。
public class BankAccount {
private double balance; // 外から直接はいじれない
public BankAccount(double initialBalance) {
if (initialBalance < 0) {
throw new IllegalArgumentException("初期残高は0以上");
}
this.balance = initialBalance;
}
public double getBalance() {
return balance; // 読み取り用だけ公開
}
public void deposit(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("預入額は1以上");
}
balance += amount;
}
public void withdraw(double amount) {
if (amount <= 0) {
throw new IllegalArgumentException("引き出し額は1以上");
}
if (amount > balance) {
throw new IllegalStateException("残高不足");
}
balance -= amount;
}
}
Javaここでは、
balance フィールドは private
残高の変更は deposit / withdraw のルールを通さないとできない
読み取りは getBalance だけに制限
としています。
こうすると、外から balance を勝手にマイナスにされたり、
ルールをすっ飛ばして異常値が入ることを防げます。
「可視性を最小にする」とは、
「外から見えてほしくないものを、きちんと隠す」ことです。
なぜ可視性を最小にすると良いのか(深掘り)
変更しやすさ:公開 API は「後から変えにくい」
public メンバーは、一度公開すると「外のコードが依存する API」になります。
メソッド名
引数の数や型
戻り値の型
などを変えると、そのメソッドを呼んでいる全コードがコンパイルエラーになります。
つまり、public を増やすほど「互換性のために守らないといけない約束」が増えていきます。
逆に private なら、「そのクラスの中だけ」で完結しているので、好きなようにリファクタリングできます。
可視性を最小にすることは、そのまま「あとから変更する自由度を残す」ことにつながります。
カプセル化:内部実装を隠せる
内部実装を隠すと、
「外の人は使い方だけ知っていればよく、中でどうやっているかは気にしなくていい」
という状態を作れます。
例えば、BankAccount の残高計算ロジックを変えたくなったとしても、
public メソッドのシグネチャ(deposit, withdraw, getBalance)が変わらなければ、
外側のコードはそのまま動きます。
実装の変更が外に漏れないのは、可視性をしっかり絞っているからです。
誤用防止とセキュリティ
public にしてしまうと、「本当は呼んでほしくない使い方」をされるリスクが増えます。
内部用のメソッド
実験的なメソッド
前提条件を満たさないと危険なメソッド
こういったものが public だと、
「何のチェックもせずにいきなり呼ばれてしまう」かもしれません。
private / package-private にしておけば、
想定している範囲以外からはそもそも呼べないので、安全性が上がります。
「どの可視性を選ぶか」の具体的な判断の仕方
ステップ 1:まず全部 private で書いてみる
クラスを書き始めるときは、
フィールドもメソッドも、まずは全部 private で書いてみるのがおすすめです。
その上で、「このメソッドは他のクラスから呼ぶ必要があるな」となったときにだけ、
可視性を一つずつ広げていきます。
他クラスからも使うけど、同じパッケージの中だけで十分なら package-private(修飾子なし)。
継承先からだけ触れたいなら protected。
本当にどこからでも使わせたい API だけ public。
という順番で考えると、「とりあえず全部 public」病を防げます。
ステップ 2:パッケージを「モジュールの単位」として考える
Java では、アクセス修飾子なし(package-private)の可視性が結構重要です。
パッケージを「1つの小さなモジュール」と見なして、
その中だけで共有したいクラスやメソッドは package-private にする、という戦略です。
例えば、ドメイン層の中でだけ使う補助クラスや、
テストしたいけれど外には見せたくない内部クラスなどは、
あえて public にせず package-private にしておく、という設計もよく使われます。
よくある「やりすぎ/やらなさすぎ」パターン
何も考えずに public にする
「とりあえず他からも使うかもしれないから public」にしてしまうのは典型的なアンチパターンです。
public にしたその瞬間から、それは「変更に気を使うべき API」になります。
本当に外から使われる必要があるか、一度立ち止まってから決めるだけで、
設計の密度がかなり変わります。
逆に、全部 private かつ getter / setter だらけにする
もう一つありがちなのは
フィールドは全部 private
でも、全部に public getter / setter を生やす
というパターンです。
この場合、見かけ上はカプセル化しているようでいて、
実際には「フィールドを public にしているのとほぼ同じ」になってしまいます。
本当に外から書き換える必要があるのか?
単純 setter ではなく、意味のある操作メソッドにしたほうがよくないか?
といった観点で、「公開する入口」自体も最小限にしていくと、
クラスの不変条件と一貫性を守りやすくなります。
まとめ:可視性を最小にするための“自分への問い”
コードを書いていてアクセス修飾子を付けるとき、
毎回自分にこう問いかけてみてください。
このフィールド/メソッドを、本当に外のクラスから触る必要があるか
アクセス範囲は「クラス内 → パッケージ内 → サブクラス → 全体」のうち、どこまで必要か
これを public にしたら、将来仕様変更するときに困らないか
このメソッドは「使わせたい API」なのか、「ただの内部実装」なのか
迷ったら、少し厳しめ(より狭い可視性)から始めて、
コンパイルエラーが出たら必要なものだけ広げる、というやり方に慣れるといいです。
