Java | オブジェクト指向:パッケージプライベート

Java Java
スポンサーリンク

全体像

パッケージプライベート(package-private)は「アクセス修飾子を何も付けない」状態の可視性で、同一パッケージ内からだけ見える(使える)という意味です。クラス・メソッド・フィールド・コンストラクタに適用され、外部パッケージからは完全に隠されます。public のように外へ広く公開せず、private のようにクラス内部に閉じもしない——“パッケージという小さな仲間内だけで共有する”ための中間の可視性です。


何に適用できるかと基本ルール

トップレベルクラスの既定はパッケージプライベート

ファイル直下(トップレベル)のクラスでアクセス修飾子を省略すると、パッケージプライベートになります。外部パッケージからは宣言自体が見えないため、インポートもできません。ライブラリの内部実装やヘルパーを隠すのに最適です。

// com.example.internal パッケージ内
package com.example.internal;

// 修飾子なし → package-private(同パッケージ内からだけ見える)
class EmailNormalizer {
    static String normalize(String s) {
        return s == null ? "" : s.trim().toLowerCase(java.util.Locale.ROOT);
    }
}
Java

メンバー(フィールド・メソッド・コンストラクタ)の既定もパッケージプライベート

クラスの中でも修飾子を省略すると、そのメンバーは同一パッケージ内からだけ参照できます。APIの表面積を減らし、外部からの誤用を防ぎます。

package com.example.internal;

public final class Sanitizer {
    // 修飾子なし → package-private
    static boolean blank(String s) { return s == null || s.trim().isEmpty(); }
}
Java

見える範囲の正確なイメージ(重要ポイントの深掘り)

同一パッケージ内だけが対象

同じパッケージに属するクラスからは、パッケージプライベートの型やメンバーを自由に参照できます。異なるパッケージからは、継承していても見えません。protected と違い、「サブクラスから自分自身に対してだけ見える」という特例はありません。

// com.example.api
package com.example.api;

public final class EmailUtil {
    public static boolean isValid(String s) {
        // 他パッケージの package-private は参照不可
        // return com.example.internal.EmailNormalizer.normalize(s).contains("@"); // コンパイルエラー
        return s != null && s.contains("@");
    }
}
Java

パッケージ設計が可視性設計になる

パッケージプライベートは“パッケージ境界”に依存します。内部実装を閉じ込めるためには、APIと実装を別パッケージに分けることが重要です。境界が整理されていれば、「内部=package-private」「外部=public」の筋が通ります。


実務での使いどころ

内部実装・ヘルパーの非公開化

利用者が直接触るべきでないクラスやメソッドは、パッケージプライベートにして隠します。公開すべき契約(API)は public の薄い窓口だけにします。

// 内部(非公開)
package com.example.internal;
class Urls {
    static boolean isHttp(java.net.URI u) { return "http".equalsIgnoreCase(u.getScheme()); }
}

// 公開(最小限の契約)
package com.example.api;
public final class Url {
    private final java.net.URI uri;
    public Url(String s) {
        try { this.uri = new java.net.URI(s); }
        catch (java.net.URISyntaxException e) { throw new IllegalArgumentException("bad url: " + s, e); }
    }
    public boolean isHttp() {
        // com.example.internal.Urls は別パッケージなので呼べない → API側で必要分だけ提供する設計に
        return "http".equalsIgnoreCase(uri.getScheme());
    }
}
Java

パッケージ内の協調(テスト支援や分割統治)

同じパッケージ内で協調して動く複数クラスの内部連携には、パッケージプライベートが向いています。テストも同一パッケージに置くことで、内部メソッドを直接検証できます(ただし公開API経由の振る舞い検証が基本)。


よくある落とし穴と設計指針(重要ポイントの深掘り)

パッケージの粒度が粗すぎて“なんでも見える”

同じ大きなパッケージに詰め込みすぎると、package-private が実質“広い公開”になり、内部漏れが増えます。レイヤ(api/domain/infrastructure)や機能単位でパッケージを分け、境界を小さく保ちましょう。

クラスを別パッケージに移動するとアクセスが切れる

パッケージプライベートはパッケージ境界に強く依存します。リファクタリングでパッケージをまたぐと、非公開メンバーへのアクセスが壊れます。境界越えが必要になったら、public の最小APIを新設して内部詳細を隠したまま外に必要機能だけ見せます。

protected と取り違えない

異なるパッケージのサブクラスからでも“自分自身に対して見える”のが protected、完全に見えないのが package-private。継承拡張ポイントを露出したいなら protected、内部連携だけに絞るなら package-private を選びます。


例題で身につける

例 1: 内部ユーティリティの隠蔽

// com.example.internal
package com.example.internal;
class Strings {
    static String normalize(String s) {
        return s == null ? "" : s.trim().toLowerCase(java.util.Locale.ROOT).replaceAll("\\s+", " ");
    }
}

// com.example.api
package com.example.api;
public final class Name {
    private final String value;
    public Name(String raw) {
        // 内部の normalize を直接使えないので、API側で必要な処理だけ持つ
        var v = raw == null ? "" : raw.trim().replaceAll("\\s+", " ");
        if (v.isEmpty()) throw new IllegalArgumentException("name required");
        this.value = v;
    }
    public String value() { return value; }
}
Java

例 2: 同一パッケージでの連携

// com.example.domain
package com.example.domain;

class Validator { // package-private
    static void checkPrice(int price) {
        if (price < 0) throw new IllegalArgumentException("price>=0");
    }
}

public final class Product {
    private int price;
    public Product(int price) {
        Validator.checkPrice(price); // 同パッケージなので参照可
        this.price = price;
    }
    public void reprice(int newPrice) {
        Validator.checkPrice(newPrice);
        this.price = newPrice;
    }
}
Java

仕上げのアドバイス(重要部分のまとめ)

パッケージプライベートは「同一パッケージだけに見せる」ための可視性です。APIは public の薄い契約だけにし、内部実装・ヘルパーは package-private に閉じる。パッケージ境界の設計が要で、粒度を適切に分けるほど可視性が効きます。protected と誤用せず、継承拡張が必要なら protected、内部連携だけなら package-private と線引きする——この基本を守れば、外に見せるべきものだけが見え、変更に強い設計になります。

タイトルとURLをコピーしました