全体像
getter / setter は「フィールド(内部状態)を外から安全に読み書きするための窓口」です。直接フィールドを公開せず、メソッド経由にすることで、読み取りは意図を伝え、書き込みは検証や前処理を挟めます。Java では命名規則が決まっており、フレームワーク(JavaBeans、ORM、シリアライザ等)が自動認識できるため、実務での互換性も高い設計になります。
基本形と命名規則
標準的な getter / setter
読み取りは getX(boolean は isX)、書き込みは setX。フィールドは private にして、外からはメソッド経由でアクセスします。
public final class User {
private String name; // 直接公開しない
public String getName() { // 読み取り
return name;
}
public void setName(String name) { // 書き込み(検証・前処理を挟める)
this.name = normalize(name);
}
private static String normalize(String s) {
return s == null ? "" : s.trim().replaceAll("\\s+", " ");
}
}
Javaboolean の命名
boolean の読み取りは isX が慣例です。書き込みは setX のままで構いません。
public final class Feature {
private boolean enabled;
public boolean isEnabled() { return enabled; }
public void setEnabled(boolean enabled) { this.enabled = enabled; }
}
Java重要ポイントの深掘り:検証とインバリアント(不変条件)
setter は「自由な代入口」ではなく「検証付きの操作」
オブジェクトの整合性を守るため、setter では必ず前提をチェックします。値の正規化や範囲チェック、関連フィールドの一貫性維持をここで行います。
public final class Product {
private int price;
public Product(int price) { setPrice(price); } // 入口を統一
public int getPrice() { return price; }
public void setPrice(int price) { // 検証を必ず通す
if (price < 0) throw new IllegalArgumentException("price>=0");
this.price = price;
}
}
Java連動する状態は一つの操作で扱う
複数フィールドの整合性が絡む場合、「個別 setter の連発」よりも、意味ある一括操作にまとめる方が安全です。
public final class Period {
private java.time.LocalDate start, end;
public java.time.LocalDate getStart() { return start; }
public java.time.LocalDate getEnd() { return end; }
public void setRange(java.time.LocalDate start, java.time.LocalDate end) { // 一括で整合性チェック
if (start == null || end == null || end.isBefore(start)) {
throw new IllegalArgumentException("end >= start");
}
this.start = start;
this.end = end;
}
}
Javaドメイン設計と getter / setter の使い分け
「何でも setter」より「意味のある操作」を優先
セッターを無制限に増やすと、オブジェクトのルールが簡単に破られます。値の変更はドメイン動詞で表現し、その中で検証・副作用を制御します。
public final class BankAccount {
private int balance;
public BankAccount(int initial) {
if (initial < 0) throw new IllegalArgumentException();
this.balance = initial;
}
public int getBalance() { return balance; }
// ドメイン操作(検証付き)
public boolean withdraw(int amount) {
if (amount <= 0 || amount > balance) return false;
balance -= amount;
return true;
}
public void deposit(int amount) {
if (amount <= 0) throw new IllegalArgumentException();
balance += amount;
}
}
JavaDTO とドメインオブジェクトで方針を分ける
- データ受け渡し用(DTO)はシンプルな getter / setter で十分。バリデーションは受け側で。
- ルールを持つドメインオブジェクトは「操作優先」、必要最小限の getter のみに絞る。
// DTO(外部入出力用)
public final class UserDto {
private String id;
private String name;
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
Java不変設計の代替案:setter をなくす選択肢
コンストラクタで確定+with メソッド(新インスタンスを返す)
不変に寄せると並行性・テストが強くなります。変更は新インスタンスを返す「with」スタイルに。
public final class Money {
private final int amount;
private final String currency;
public Money(int amount, String currency) {
if (amount < 0 || currency == null || currency.isBlank()) throw new IllegalArgumentException();
this.amount = amount;
this.currency = currency;
}
public int getAmount() { return amount; }
public String getCurrency() { return currency; }
public Money withAmount(int newAmount) { // setter の代わり
return new Money(newAmount, currency);
}
}
Javarecord の活用(値オブジェクトを簡潔に)
値オブジェクトは record で短く書け、getter 相当のアクセサのみ(不変)になります。検証が必要ならコンパクトコンストラクタを使います。
public record Point(int x, int y) {
public Point {
if (x < 0 || y < 0) throw new IllegalArgumentException("non-negative");
}
}
Javaフレームワークとの相性(JavaBeans の前提)
JavaBeans 規約に沿うと便利
多くのフレームワークは「private フィールド+public getX/setX」を前提に自動マッピングします。命名と可視性を守ると、設定やシリアライズがスムーズです。
public final class Config {
private String region;
private int timeoutMs;
public String getRegion() { return region; }
public void setRegion(String region) { this.region = region; }
public int getTimeoutMs() { return timeoutMs; }
public void setTimeoutMs(int timeoutMs) { this.timeoutMs = timeoutMs; }
}
Java必要なら可視性を絞る
JPA などで「引数なしコンストラクタ/プロパティアクセス」が必要でも、ドメイン側の操作は別に用意し、整合性を守る設計にします(protected コンストラクタ、検証付きメソッドなど)。
よくある落とし穴と回避
「何でも公開 setter」は危険
セッターを無差別に公開すると、オブジェクトの前提が壊れます。必要最小限に絞り、検証・前処理を必ず入れる。
セッターから例外方針が曖昧
失敗時は例外を投げるか、戻り値で知らせるかを統一。契約(受け入れる値、失敗の扱い)を明確にする。
連動するフィールドの一貫性崩壊
関連する値は一括操作にまとめ、個別 setter の並行使用で矛盾が生じないようにする。
性能最適化のためにフィールドを public にする
整合性が壊れる典型。getter は JIT でインラインされやすく、性能上の差は小さいことが多い。安全性を優先。
例題で身につける
例 1: 正規化を setter に集約して重複排除
public final class Person {
private String name;
public String getName() { return name; }
public void setName(String name) {
var v = name == null ? "" : name.trim().replaceAll("\\s+", " ");
if (v.isEmpty()) throw new IllegalArgumentException("name required");
this.name = v;
}
}
Java例 2: 整合性を守る一括操作と最小 getter
public final class Rectangle {
private int w, h;
public int getW() { return w; }
public int getH() { return h; }
public void setSize(int w, int h) {
if (w <= 0 || h <= 0) throw new IllegalArgumentException("positive size");
this.w = w; this.h = h;
}
public int area() { return w * h; } // 計算は読み取り専用のAPIで提供
}
Java例 3: 不変オブジェクトで「with」スタイル
public final class Config {
private final String region;
private final int timeoutMs;
public Config(String region, int timeoutMs) {
if (region == null || region.isBlank() || timeoutMs <= 0) throw new IllegalArgumentException();
this.region = region.trim();
this.timeoutMs = timeoutMs;
}
public String getRegion() { return region; }
public int getTimeoutMs() { return timeoutMs; }
public Config withTimeout(int ms) { return new Config(region, ms); } // setter なし
}
Java仕上げのアドバイス(重要部分のまとめ)
getter / setter は「外と中のあいだに検証と前処理の壁を作るための窓口」。フィールドは private にし、setter は必ず検証付き、連動する状態は一括操作へ。ドメインでは「意味のある操作」を優先し、DTO など受け渡し用はシンプルな getter / setter。不変が向く場面では setter を廃して with メソッドや record を活用する——この線引きができると、整合性が守られ、フレームワーク互換性と保守性の両立ができます。
