全体像
アクセス修飾子は「どこからそのクラス・フィールド・メソッドが見えるか(使えるか)」を制御するための言語機能です。公開範囲を絞ることで、意図しない使われ方や整合性の破壊を防ぎ、APIの進化に強くできます。Java の基本は public、protected、package-private(修飾子なし)、private の4種類。トップレベルのクラスに使えるのは public と package-private のみです。
種類と見える範囲の違い
public(どこからでも見える)
クラス・メンバーがどのパッケージからも参照可能。ライブラリの公開APIやエントリポイントに使います。
// 別パッケージからも利用可能
public class User {
public String name() { return "Taro"; }
}
Java- 使いどころ: 外部に提供する契約(API)
- 注意点: 一度公開すると後方互換性の制約が重くなるため、むやみに増やさない
package-private(修飾子なし:同一パッケージ内だけ)
修飾子を付けない場合の既定。パッケージ内部の協調のために使い、外部からは隠します。
// top-level クラスに多用。内部モジュール用
class InternalService {
String run() { return "ok"; }
}
Java- 使いどころ: 実装詳細、ヘルパー、テスト支援のダブル
- 注意点: パッケージの境界設計が重要(粒度を適切に)
protected(同パッケージ+サブクラスから)
同一パッケージ内では通常に参照可能。異なるパッケージの場合は「サブクラスから自分自身(this)に対して」参照可能です。
package a;
public class Base {
protected String label = "base";
protected String name() { return label; }
}
package b;
public class Sub extends a.Base {
public String show() {
return this.name(); // OK(継承で this に対して)
// new a.Base().name(); // NG(他インスタンスの protected には触れない)
}
}
Java- 使いどころ: 継承前提の拡張ポイント(テンプレートメソッドなど)
- 注意点: 外部からの乱用を防ぐため、protected にする前に「委譲+public 最小」や「package-private で同一パッケージ運用」を検討
private(同一クラス内だけ)
クラスの内部実装を完全に隠します。カプセル化の要。
public final class Account {
private int balance; // 直接触らせない
public int balance() { return balance; }
public void deposit(int amount) {
if (amount <= 0) throw new IllegalArgumentException();
balance += amount;
}
}
Java- 使いどころ: フィールド、実装詳細、ヘルパーメソッド
- 注意点: テストのために可視性を緩めない(パッケージ境界や公開メソッドで検証する)
トップレベルとネストの違い(クラス定義の文脈)
トップレベルのクラスに使える修飾子
トップレベル(ファイル直下)のクラスは public か package-private(修飾子なし)のみ。private・protected は不可です。
// 公開API
public class Api { /* ... */ }
// 内部用(修飾子なし)
class Impl { /* ... */ }
Javaネスト(内部)クラスの可視性
内部クラス(static/非static)のメンバーは、private も含めて柔軟に制御できます。外からは隠し、外側クラスだけに見せる設計が可能です。
public class Parser {
private static class Token { /* Parser 内専用 */ }
public Result parse(String s) { /* Token を内部で使用 */ return new Result(); }
}
Java設計の指針(重要ポイントの深掘り)
まずは最小公開(private → package-private → protected → public)
- 原則: 初期は private で閉じ、必要になった範囲にだけ段階的に広げる
- 効果: 依存が減り、変更が容易になる。外部の破壊的利用を防げる
フィールドは原則 private、操作は検証付きメソッドで
- 狙い: 整合性を守る(直接代入の禁止)
- 例: ゲッター・セッターではなく「意味のある操作」を公開
public final class Product {
private int price;
public void reprice(int newPrice) { if (newPrice >= 0) this.price = newPrice; }
}
Javaprotected は継承のための契約。乱用しない
- 狙い: 拡張ポイントだけを意図的に露出
- 代替: インターフェース+委譲(テスト容易・差し替え自由)
パッケージ設計が可視性設計
- 狙い: package-private を活用し、内部実装はパッケージに閉じる
- 実務: domain/infrastructure/app などレイヤごとにパッケージを分けると境界が明確
例題で体験する可視性の差
例 1: ライブラリの公開APIと内部実装の分離
// com.example.api
package com.example.api;
public final class Email {
private final String value;
public Email(String raw) {
var v = raw == null ? "" : raw.trim().toLowerCase();
if (!v.contains("@")) throw new IllegalArgumentException();
this.value = v;
}
public String value() { return value; }
}
// com.example.internal
package com.example.internal;
class EmailNormalizer { // 外から見えない
static String normalize(String s) { return s == null ? "" : s.trim().toLowerCase(); }
}
JavaAPIは public、内部ヘルパーは package-private にして外から隠します。
例 2: 継承で protected を使う拡張ポイント
package app;
public abstract class Shape {
protected abstract double area(); // サブクラスが実装
public String describe() { return "area=" + area(); } // 共通の公開API
}
public final class Circle extends Shape {
private final double r;
public Circle(double r) { this.r = r; }
@Override protected double area() { return Math.PI * r * r; }
}
Java外へ見せるのは public describe()、実装の差分は protected area() に限定。
例 3: フィールドを private にして操作で公開
public final class Cart {
private final java.util.List<Item> items = new java.util.ArrayList<>();
public void add(Item it) {
if (it == null || it.price() < 0) throw new IllegalArgumentException();
items.add(it);
}
public int total() { return items.stream().mapToInt(Item::price).sum(); }
}
public record Item(String name, int price) {}
Java直接 items を公開せず、意味のある操作だけ公開します。
よくあるつまずきと回避
なんでも public にしてしまう
見えるほど依存が増え、変更が困難に。最小公開を徹底し、外に見せるのは「契約」だけに絞る。
protected の誤解(他インスタンスに触れる)
異なるパッケージでは、サブクラスから「自分の継承メンバー」だけ触れる。親型の別インスタンスの protected には触れない。
フィールドを public にする
整合性が壊れる。必ず private で閉じ、検証付きメソッドで操作。
テストのために可視性を緩める
テストは公開API経由で振る舞いを検証。どうしても内部を見るならパッケージ構成を見直すか、テスト用ファクトリを検討。
仕上げのアドバイス(重要部分のまとめ)
アクセス修飾子は「依存の境界」を作る道具。クラスの公開は最小に、フィールドは private、実装詳細は package-private に閉じ、継承ポイントだけを protected で露出。public は契約に限る。これだけで、読みやすさ・安全性・変更耐性が跳ね上がります。
