4-16:publicのクラスでは、publicのフィールドではなく、アクセッサ―メソッドを使う
要点
public なクラスで公開するデータは public フィールドにせずアクセサ(getter/setter)メソッドで公開する。将来の保守性・安全性・後方互換性のためのベストプラクティス。
なぜ public フィールドがまずいのか
public フィールドをそのまま公開すると、「実装(データ表現)」をそのまま API として固定してしまう。
後で内部を変えたくても、公開フィールドはクライアントコードが直接参照しているため変更が難しく、バグや互換性壊れに繋がる。アクセサにしておけば実装を隠して自由に変えられる。
悪い例
// 悪い:public フィールドをそのまま公開している
public class Point {
public int x;
public int y;
}
問題点の例:
- 後で x,y の計算方法を変えたい(たとえば内部で極座標を使いたい)と、公開フィールドだと不可能。
- 不変にしたくても既に直接代入されているコードがあると困る。
- 値検証・ログ・スレッド同期など処理を挟めない。
良い例
public final class Point {
private final int x;
private final int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() { return x; }
public int getY() { return y; }
// もし将来内部表現を変えたくなっても
// クライアントは getX()/getY() を呼ぶだけでよい
}
利点:
- 実装の隠蔽(将来内部を変更できる)
- 不変化(final)でスレッドセーフを簡単に実現できる
- 返却値を加工(防御的コピー、不変ビュー)できる
アクセサにすることで得られる具体的メリット
1. 将来の実装変更が容易
内部を配列や計算値に変えても、getX()/getY() の契約を守ればクライアントは影響を受けない。
2. 入力検証・不変化・ライフサイクル管理ができる
setter に検証ロジックを入れたり、不要になった参照を切ったりできる(public フィールドでは不可)。
3. 遅延初期化・キャッシュが可能
getter 内で必要なときに初期化やキャッシュを行える(性能上の工夫が可能)。
4. スレッドセーフ実装がしやすい
getter/setter に synchronized や volatile を使える。public フィールドはその場で制御できない。
5. 型の抽象化・拡張が可能
フィールドが List なら getter は List の不変ビューを返す、将来内部を違うコレクションに変えても外部は影響を受けない。
6. インタフェース化しやすい
メソッドはインタフェースに載せられる(API 設計の柔軟性)。public フィールドはインタフェースにできない。
例:コレクションを返す場合の注意
悪い:
public class Team {
public List<Player> players; // NG: 直接書き換えられる
}
良い:
public class Team {
private final List<Player> players = new ArrayList<>();
// 読み取り用は不変ビューを返す(防御的コピーの代わり)
public List<Player> getPlayers() {
return Collections.unmodifiableList(players);
}
// 追加/削除はメソッドで管理
public void addPlayer(Player p) { players.add(Objects.requireNonNull(p)); }
}
例外:公開して良いフィールド
- public static final な定数(Math.PI のような不変コンスタント)は例外。
- ただしインスタンスフィールドで public を許すべきケースは極めて稀
バイナリ互換性の観点
- 初めから public フィールドを出すと、後でそれをメソッドに置き換えると**バイナリ互換性(既コンパイル済みクライアントが動かなくなる)**を壊すことがある。
- だからライブラリ設計時は最初からアクセサで公開しておくのが安全。
まとめ
- public クラスでは public フィールドを避け、アクセサメソッドを使う。
- これにより実装の隠蔽、将来の変更、スレッド安全性、検証・キャッシュ、API の柔軟性が得られる。
- 例外は public static final な定数だけ。