22.

アクセス修飾子完全ガイド — public/private/protected

編集
この記事の要点
  • アクセス修飾子は クラス・メソッド・フィールドの公開範囲を制御する
  • Java: public / protected / package-private (デフォルト) / private の 4 段階
  • C# は internal (同アセンブリ内) も持つ
  • 原則: 最小公開 — フィールドは private、外部 API は最小限の public
  • Python は規約ベース (_protected, __private)、本当の隠蔽はできない

Java のアクセス修飾子

修飾子同一クラス同一パッケージ子クラス (他パッケージ)他パッケージ
public
protected
package-private (なし)
private
package com.example.app;

public class User {
    public  String name;          // どこからでも見える
    protected int age;            // 同パッケージ + 子クラス
    String email;                 // package-private (同パッケージのみ)
    private String password;      // クラス内のみ

    public void publicMethod()        { }
    protected void hookForSubclass()  { }
    void packageMethod()              { }   // package-private
    private void internalLogic()      { }
}

クラスへの適用

クラス宣言は public または package-private のみ (トップレベルクラス):

// ファイル User.java
public class User { }       // public — どこからでも参照可
class UserHelper { }        // package-private — 同パッケージのみ
// private class X { }      // ★ トップレベルでは NG (内部クラスでは可)

// 内部クラスはどの修飾子も使える
public class Outer {
    public  class A { }
    protected class B { }
    class C { }
    private class D { }    // 外から参照不可
}

フィールドのカプセル化

// ❌ public フィールドは外部から自由に書き換え可能 → 不変条件を破られる
public class BadUser {
    public int age;   // age = -100 等の不正値を防げない
}

// ✅ private + getter/setter
public class User {
    private int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age < 0 || age > 150) {
            throw new IllegalArgumentException("age out of range");
        }
        this.age = age;
    }
}

// 完全に書き換えさせたくない場合は setter 削除 + final
public class ImmutableUser {
    private final String name;
    private final int age;

    public ImmutableUser(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }
    public int getAge()     { return age; }
}

protected の使いどころ

// テンプレートメソッドパターン — フックを protected で公開
public abstract class Report {
    public final void render() {
        printHeader();
        printBody();    // ★ 子に必ず実装させる
        printFooter();
    }

    protected void printHeader() { System.out.println("===HEADER==="); }
    protected abstract void printBody();
    protected void printFooter() { System.out.println("===FOOTER==="); }
}

class SalesReport extends Report {
    @Override
    protected void printBody() {
        System.out.println("Sales: 1000");
    }
}

JavaBeans 規約

Java のフレームワーク (Spring / Jackson / JSP EL) が認識する標準パターン:

  • 引数なし public コンストラクタ
  • フィールドは private
  • アクセサ getXxx() / setXxx(value)public
  • boolean は isXxx() でも OK
public class User {
    private String name;
    private boolean active;

    public User() {}

    public String getName()              { return name; }
    public void   setName(String name)   { this.name = name; }

    public boolean isActive()            { return active; }
    public void    setActive(boolean a)  { this.active = a; }
}

テスト時の private 問題

public class Calculator {
    private int internalState = 0;
    private int compute(int x) { return x * 2; }
}

// ★ 直接テストできない (private)
// 解決策:
// 1. ロジックを抽出して別クラスに (推奨)
// 2. アクセス修飾子を package-private に緩和 + テストを同パッケージへ
// 3. Reflection でアクセス (最終手段)

import java.lang.reflect.Method;
import java.lang.reflect.Field;

@Test
void test() throws Exception {
    Calculator c = new Calculator();

    // private メソッド呼び出し
    Method m = Calculator.class.getDeclaredMethod("compute", int.class);
    m.setAccessible(true);
    int r = (int) m.invoke(c, 5);
    assertEquals(10, r);

    // private フィールドアクセス
    Field f = Calculator.class.getDeclaredField("internalState");
    f.setAccessible(true);
    f.set(c, 42);
}

言語別比較

言語修飾子備考
Javapublic / protected / (なし) / private4 段階
PHPpublic / protected / privateJava 同等 (パッケージ概念なし)
C#public / protected / internal / protected internal / private / private protected★ 6 段階
Python慣習: _x / __x★ 言語レベルの強制なし
JavaScript慣習 → ES2022 #field で真の privateクラスフィールドの prefix で隠蔽
Kotlinpublic / internal / protected / privateinternal は同モジュール
class User {
    public  string $name;
    protected int $age;
    private  string $password;

    public function getName(): string    { return $this->name; }
    private function hashPassword(): string { ... }
}
class User:
    def __init__(self, name, password):
        self.name = name           # public 扱い
        self._internal = 0         # protected 慣習 (アンダースコア 1 つ)
        self.__password = password # private 慣習 (アンダースコア 2 つ → name mangling)

u = User("Taro", "secret")
print(u.name)        # OK
print(u._internal)   # ★ 警告なくアクセス可 (慣習違反)
print(u.__password)  # ★ AttributeError
print(u._User__password)  # ★ name mangling で実は読める (完全な隠蔽ではない)
public class User {
    public  string Name { get; set; }
    protected int Age;
    internal string SessionId;            // 同アセンブリのみ
    protected internal string Region;     // 同アセンブリ or 子クラス
    private string password;
    private protected string secret;      // 同アセンブリ かつ 子クラス
}

JavaScript ES2022 真の private フィールド

class User {
    #password;          // ★ # は真の private

    constructor(name, password) {
        this.name = name;
        this.#password = password;
    }

    checkPassword(input) {
        return this.#password === input;
    }
}

const u = new User("Taro", "abc");
console.log(u.name);        // OK
console.log(u.#password);   // ★ SyntaxError (構文レベルで弾かれる)

設計指針

  • 原則: 最小公開。必要になったら緩和、最初は private に
  • フィールドは原則 private。可視化が必要なら getter/setter または record
  • API は public、内部実装は private/package-private
  • 継承想定のフックは protected で公開
  • 同パッケージ専用ヘルパーは package-private
  • テストはプロダクションコードと同じパッケージに置くことで package-private にもアクセス可

FAQ

Q: Java で「同じプロジェクト内なら見える」修飾子は?
A: 存在しない。internal は C# / Kotlin の機能。Java はパッケージ単位 (package-private) でしか共有できない。

Q: private メソッドはオーバーライドできる?
A: できない (子クラスから見えないため)。同名メソッドを子で書くことは可能だが、オーバーライドではなく別物 (隠蔽でもない)。

Q: public final フィールドは安全?
A: 基本型や不変オブジェクトなら OK (例: public static final int MAX = 100)。可変オブジェクトでは中身を変更されるので避ける。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 基本的なルール
  2. データ型
  3. 変数
  4. 定数
  5. 配列
  6. コレクション(List,Set,Queue)
  7. Map(連想配列)
  8. 演算子
  9. 条件分岐
  10. 繰り返し制御文
  11. クラス
  12. メソッド
  13. インスタンス化
  14. コンストラクタ
  15. staticキーワード
  16. オーバーロード
  17. 継承
  18. オーバーライド
  19. this
  20. super
  21. パッケージ
  22. アクセス修飾子
  23. 抽象クラス・メソッド
  24. インターフェース
  25. カプセル化
  26. データベース接続
  27. セッション
  28. ファイル入出力
  29. ラムダ式

最近更新/作成されたページ