ページの作成
親となるページを選択してください。
親ページに紐づくページを子ページといいます。
例: 親=スポーツ, 子1=サッカー, 子2=野球
子ページを親ページとして更に子ページを作成することも可能です。
例: 親=サッカー, 子=サッカーのルール
親ページはいつでも変更することが可能なのでとりあえず作ってみましょう!
| この記事の要点 |
|
カプセル化とは
カプセル化 (Encapsulation) とは、オブジェクトの内部データ (フィールド) を外部から直接触れないように隠し、決められた手続き (メソッド) を通してのみアクセスを許す設計原則です。継承・ポリモーフィズム・抽象化と並ぶ OOP の 4 大原則の 1 つに数えられます。
カプセル化を守ることで:
- 不変条件 (invariant) を保てる — 残高がマイナスにならない、年齢が 0〜150 の範囲、など
- 内部実装を変更しても外部 API に影響しない — フィールド名変更・データ構造変更が自由に
- 並行アクセスや競合の制御点を 1 つに集約できる
- テスト・ログ追加・バリデーション差し込みが容易
具体例1: 銀行口座 (Bank Account) — 古典的な例
最も典型的なのは銀行口座クラスです。残高 balance を public にすると、外部から自由に書き換えられてしまい、マイナス残高や予期しない値になり得ます。
// 悪い例: カプセル化されていない
public class BadAccount {
public long balance; // 誰でも触れる
}
BadAccount a = new BadAccount();
a.balance = -1_000_000; // マイナス残高が許される
a.balance = Long.MAX_VALUE; // オーバーフロー間近
// 良い例: カプセル化された Account
public class Account {
private long balance; // 隠蔽
private final String accountNo;
public Account(String accountNo, long initial) {
if (initial < 0) throw new IllegalArgumentException("初期残高は 0 以上");
this.accountNo = accountNo;
this.balance = initial;
}
public long getBalance() {
return balance;
}
public void deposit(long amount) {
if (amount <= 0) throw new IllegalArgumentException("入金額は正の数");
balance += amount;
}
public void withdraw(long amount) {
if (amount <= 0) throw new IllegalArgumentException("出金額は正の数");
if (balance < amount) throw new IllegalStateException("残高不足: " + balance);
balance -= amount;
}
}
Account a = new Account("001", 1000);
a.deposit(500); // OK
a.withdraw(200); // OK
// a.balance = -1; ← コンパイルエラー: private
残高へのあらゆる変更が deposit / withdraw を通るため、不変条件 (残高 >= 0) を常に保てます。
具体例2: 不変オブジェクト (Immutable)
setter を一切作らず、生成時に決めた値を変えられないオブジェクトを不変オブジェクトと呼びます。スレッドセーフかつバグの混入余地が少なく、現代の OOP では強く推奨されます。
import java.time.LocalDate;
import java.util.Objects;
public final class Person { // final で継承禁止
private final String name; // final で再代入禁止
private final LocalDate birthDate;
public Person(String name, LocalDate birthDate) {
this.name = Objects.requireNonNull(name);
this.birthDate = Objects.requireNonNull(birthDate);
}
public String getName() { return name; }
public LocalDate getBirthDate() { return birthDate; }
// 名前変更は「新しいインスタンスを返す」
public Person withName(String newName) {
return new Person(newName, this.birthDate);
}
}
Person p1 = new Person("太郎", LocalDate.of(1990, 1, 1));
Person p2 = p1.withName("次郎"); // p1 はそのまま、p2 は新しいインスタンス
標準ライブラリの不変クラス例:
| クラス | 特徴 |
|---|---|
java.lang.String | 文字列。連結や置換は新しい String を返す |
java.time.LocalDate / LocalDateTime / Instant | 日時の不変クラス (Java 8+) |
java.math.BigDecimal / BigInteger | 任意精度の不変数値 |
Integer / Long / Double 等のラッパー | プリミティブのボックス化版 |
List.of() / Map.of() / Set.of() (Java 9+) | 変更不可コレクション |
具体例3: Java records (Java 14+) — 自動カプセル化
records は不変なデータキャリアを 1 行で宣言できる構文。等価性 (equals) / ハッシュ / 文字列化 / アクセサ / コンストラクタが自動生成されます。
// Java 14+
public record Money(long amount, String currency) {
// コンパクトコンストラクタで検証
public Money {
if (amount < 0) throw new IllegalArgumentException("負の金額不可");
Objects.requireNonNull(currency);
}
}
Money m = new Money(1000, "JPY");
System.out.println(m.amount()); // → 1000 (getAmount ではなくフィールド名)
System.out.println(m.currency()); // → JPY
System.out.println(m); // → Money[amount=1000, currency=JPY]
// m.amount = 2000; ← コンパイルエラー: record は不変
具体例4: Builder Pattern
フィールドが多いと、不変オブジェクトでもコンストラクタ引数が長大になります。Builder Pattern で読みやすさと不変性を両立:
public final class HttpRequest {
private final String url;
private final String method;
private final Map<String, String> headers;
private final String body;
private HttpRequest(Builder b) {
this.url = b.url;
this.method = b.method;
this.headers = Map.copyOf(b.headers); // 防御的コピー
this.body = b.body;
}
public static Builder builder() { return new Builder(); }
public static class Builder {
private String url;
private String method = "GET";
private Map<String, String> headers = new HashMap<>();
private String body = "";
public Builder url(String u) { this.url = u; return this; }
public Builder method(String m) { this.method = m; return this; }
public Builder header(String k, String v) { this.headers.put(k, v); return this; }
public Builder body(String b) { this.body = b; return this; }
public HttpRequest build() {
if (url == null) throw new IllegalStateException("url 必須");
return new HttpRequest(this);
}
}
}
HttpRequest req = HttpRequest.builder()
.url("https://example.com")
.method("POST")
.header("Content-Type", "application/json")
.body("{}")
.build();
具体例5: PHP 8.1 readonly
PHP 8.1 では readonly 修飾子で「初期化後は変更不可」のプロパティを宣言できます (Java の final 相当)。Java の records に近い感覚で不変クラスを書けます。
class Point {
public function __construct(
public readonly float $x,
public readonly float $y,
) {}
public function translate(float $dx, float $dy): Point {
return new Point($this->x + $dx, $this->y + $dy);
}
}
$p = new Point(1.0, 2.0);
echo $p->x; // 1
// $p->x = 5; // Error: Cannot modify readonly property
// PHP 8.2 では「クラス丸ごと readonly」も可能
final readonly class Money {
public function __construct(
public int $amount,
public string $currency,
) {}
}
具体例6: 防御的コピー (Defensive Copy)
不変クラスで List / Map など可変コレクションを保持する場合、受け取った参照をそのまま持つと外から書き換えられて不変性が崩れます。
public final class Team {
private final List<String> members;
// ❌ 危険: 参照を共有
public Team(List<String> members) {
this.members = members; // 外から add される可能性
}
// ✅ 防御的コピー
public Team(List<String> members) {
this.members = List.copyOf(members); // Java 10+ : 不変コピー
}
public List<String> getMembers() {
// ✅ そのまま返しても List.copyOf() なので変更不可
return members;
}
}
List<String> src = new ArrayList<>(List.of("Alice", "Bob"));
Team t = new Team(src);
src.add("Charlie"); // ✅ t には影響しない
t.getMembers().add("Dave"); // UnsupportedOperationException
カプセル化のレベル比較
| レベル | 形 | 用途 |
|---|---|---|
| 0 (無し) | public int x; | 非推奨。テスト用構造体程度 |
| 1 (基本) | private + getter/setter | JavaBeans、Hibernate エンティティ |
| 2 (検証付き) | setter で不変条件チェック | 業務エンティティ |
| 3 (不変) | setter 無し、final / readonly | 値オブジェクト (Value Object) |
| 4 (Builder) | 不変 + Builder で組立 | フィールド多数の Config 系 |
| 5 (record / readonly class) | 言語機能で完結 | DTO / レスポンス型 |
FAQ
Q: getter/setter を全フィールドに付けるのは「カプセル化」と言える?
A: 形だけで実質はノーガード。setter で不変条件を検証するか、思い切って不変クラスにするのが本来のカプセル化です。
Q: Lombok の @Data は使うべき?
A: 手早く JavaBeans を作るには便利ですが、setter が全公開されるので業務エンティティには不向き。@Value (不変) や records を優先しましょう。
Q: 不変オブジェクトはメモリを食わない?
A: 大量に新インスタンスを作るとオーバーヘッドがありますが、JVM の世代別 GC は短命オブジェクトに強いため、ほとんどのケースで性能問題にはなりません。むしろスレッドセーフ性とバグ削減のメリットが圧倒的に大きいです。
ページの作成
親となるページを選択してください。
親ページに紐づくページを子ページといいます。
例: 親=スポーツ, 子1=サッカー, 子2=野球
子ページを親ページとして更に子ページを作成することも可能です。
例: 親=サッカー, 子=サッカーのルール
親ページはいつでも変更することが可能なのでとりあえず作ってみましょう!
子ページはありません
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript base URL 取得方法|window.location.origin と SSR/Node.js 対応
- 4 YouTube Data API v3 エラー一覧|403/400/404 の主要原因と切り分け
- 5 Spring Frameworkのアノテーション一覧
- 6 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 7 3Dグラフィックスとは|モデリング/レンダリング/主要ソフトウェア (Blender / Maya)
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- IPv6とは|128bitアドレス・コロン16進表記/::省略・リンクローカル・SLAAC・デュアルスタック NEW 2026-06-22 12:34:44
- MAC アドレスフィルタリングの仕組みと限界 | ネットワーク入門 NEW 2026-06-22 12:19:10
- VPNとは|暗号トンネル・サイト間/リモートアクセス・IPsec/SSL-VPN/WireGuardを解説 NEW 2026-06-22 12:19:10
- WebRTC とは ブラウザ間 P2P の音声・映像・データ通信 | ネットワーク入門 NEW 2026-06-22 12:17:25
- HTTP/2 とは 多重化・HPACK・バイナリフレーム | ネットワーク入門 NEW 2026-06-22 12:17:25
- Web通信プロトコル入門 HTTP/2・HTTP/3・WebSocket・gRPC・WebRTC | ネットワーク入門 NEW 2026-06-22 12:17:25
- gRPC とは HTTP/2 + Protocol Buffers の高速 RPC | ネットワーク入門 NEW 2026-06-22 12:17:25
- HTTP/3 (QUIC) とは UDP ベースの低遅延 Web 通信 | ネットワーク入門 NEW 2026-06-22 12:17:25
- WebSocket とは 全二重リアルタイム通信 ws/wss | ネットワーク入門 NEW 2026-06-22 12:17:25
- 証明書と認証局(CA)とは|X.509・信頼チェーン・DV/OV/EV・失効(CRL/OCSP)を解説 NEW 2026-06-22 12:17:24
- ファイアウォールとは|パケットフィルタ・ステートフル・DMZ・次世代FW(L4/L7)を解説 NEW 2026-06-22 12:17:24
- iptables/nftablesとは|テーブル・チェーン・ルール例・永続化をLinux視点で解説 NEW 2026-06-22 12:17:24
- HAProxy とは frontend/backend と設定例 | ネットワーク入門 NEW 2026-06-22 12:17:24
- CDN とは エッジキャッシュ・TTL・Cloudflare/CloudFront | ネットワーク入門 NEW 2026-06-22 12:17:24
- TLS/SSLの仕組み|ハンドシェイク・暗号スイート・前方秘匿性・証明書検証をわかりやすく解説 NEW 2026-06-22 12:17:24
コメントを削除してもよろしいでしょうか?