本ページはプロモーションが含まれています

Java入門

Javaアクセス修飾子4つの違いと使い分けを完全解説

トム

・都内自社開発IT企業勤務/javaのバックエンドエンジニア
/java歴10年以上 ・首都圏在住30代
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎

Javaの学習を始めたばかりのころ、私はpublicprivateといったキーワードを「おまじない」のように書いていました。「なぜここはprivateなのですか?」と問われ、しどろもどろになった経験は今でも忘れられません。

多くのJava初学者が、このアクセス修飾子の使い分けで一度はつまずくのではないでしょうか。

この記事は、過去の私のように

  • publicprivateの違いが曖昧な方
  • なんとなくでアクセス修飾子を選んでしまっている方
  • protecteddefaultの使いどころが分からない方

に向けて書いています。

この記事を読み終えるころには、4種類のJavaアクセス修飾子の違いが明確に理解でき、自信を持って使い分けられるようになります。安全で保守性の高いコードを書くための第一歩を、ここから踏み出しましょう。

アクセス修飾子とは?Javaにおける基本概念を理解しよう

Javaにおけるアクセス修飾子とは、クラスやフィールド、メソッドへのアクセスをどこから許可するかを制御するためのキーワードです。文字通り、アクセスを「修飾」し、制限をかける役割を持っています。

アクセス修飾子の役割と必要性

アクセス修飾子の最も重要な役割は、プログラムの安全性を高めることです。もし、どこからでも自由にクラスの内部データにアクセスできてしまうと、意図しない場所で値が書き換えられ、予期せぬバグやエラーの原因になります。

たとえば、銀行口座の残高を管理するプログラムを考えてみましょう。残高という重要なデータを誰でも直接変更できたら、システムは成り立ちません。「入金」や「出金」といった決められた手続き(メソッド)を通してのみ操作できるように制限する必要があります。

このように、アクセス修飾子は、外部から勝手に触られたくない重要なデータを守り、決められたルールで安全にプログラムを動かすために不可欠な仕組みなのです。

Javaで使える4種類のアクセス修飾子

Javaには、アクセスの制限範囲が異なる4種類のアクセス修飾子が存在します。

ポイント

  1. public (パブリック)
  2. protected (プロテクテッド)
  3. default (デフォルト) ※キーワードの記述は不要
  4. private (プライベート)

これらのアクセス修飾子は、制限が緩いものから厳しいものへと段階的に設定されており、状況に応じて使い分けることが重要です。

カプセル化とアクセス修飾子の関係

カプセル化は、オブジェクト指向プログラミングにおける非常に重要な概念の一つです。これは、クラスの内部データ(フィールド)と、そのデータを操作する手続き(メソッド)を一つにまとめ、内部の詳細を外部から隠蔽する考え方を指します。

このカプセル化を実現するために、アクセス修飾子が活躍します。具体的には、フィールドをprivateにして外部から直接アクセスできないように隠蔽し、そのフィールドを操作するためのpublicなメソッド(ゲッターやセッター)を公開します。

これにより、クラスの利用者は内部構造を意識することなく、公開されたメソッドを通じて安全にデータを操作できます。カプセル化は、プログラムの独立性を高め、仕様変更に強い、保守性の高いコードを作成するための基本原則です。

Javaのアクセス修飾子の種類と違い

ここでは、4種類のアクセス修飾子が持つアクセス制限の範囲と、それぞれの違いについて詳しく見ていきましょう。

修飾子同一クラス内同一パッケージ内サブクラス(別パッケージ)どこからでも
public
protected×
default××
private×××

public(パブリック)|どこからでもアクセス可能

publicは、最もアクセス制限が緩い修飾子です。publicが付けられたクラス、フィールド、メソッドは、プロジェクト内のどのクラスからでもアクセスできます。

ライブラリとして提供されるクラスや、プログラムの主要なエントリーポイントとなるメソッドなど、外部に公開して自由に使ってもらいたい機能に対して使用します。APIとして機能する部分と考えると分かりやすいでしょう。

protected(プロテクテッド)|継承関係でアクセスを制限

protectedは少し特殊なアクセス修飾子です。同一パッケージ内のクラス、または別パッケージであってもそのクラスを継承したサブクラスからアクセスが可能です。

この特性から、主に継承関係にあるクラス間で、特定の情報や機能を共有したい場合に使われます。フレームワークやライブラリなどで、拡張性を考慮した設計を行う際によく見られます。

default(デフォルト)|同一パッケージ内からのみアクセス可能

defaultは、アクセス修飾子を何も記述しなかった場合に適用されるアクセスレベルです。package-private(パッケージプライベート)とも呼ばれます。

defaultのメンバーは、同じパッケージに所属するクラスからのみアクセス可能です。パッケージというまとまりの中でだけ利用される補助的なクラスや、内部的な処理で利用するメソッドなどに適しています。パッケージの外に公開する必要はないが、パッケージ内では共有したい、という場面で有効です。

private(プライベート)|クラス内のみアクセス可能

privateは、最もアクセス制限が厳しい修飾子です。privateが付けられたメンバーは、そのメンバーが定義されているクラスの内部からしかアクセスできません

クラスの外部に公開する必要のない、内部的な状態を保持するフィールドや、そのクラス内だけで使われる補助的なメソッドに使用します。前述したカプセル化の原則に従い、フィールドは原則としてprivateにするのがJavaプログラミングの基本です。これにより、クラスの安全性が大幅に向上します。

アクセス修飾子の使い分け方と実例

理論を学んだところで、次に実際のプログラミングでどのようにアクセス修飾子を使い分ければよいのか、具体的な例を挙げて解説します。

クラスとメソッドでのアクセス修飾子の使い方

クラスに付けられるアクセス修飾子はpublicdefaultの2種類です。

  • publicクラス: パッケージの外部に公開し、誰でも利用できるようにしたいクラスに使います。ほとんどの主要なクラスはpublicで定義されます。
  • defaultクラス: 同じパッケージ内でのみ利用される、補助的なクラスに使います。そのパッケージの機能を実装するための「部品」のようなイメージです。

メソッドのアクセス修飾子は、そのメソッドの役割によって決まります。

  • publicメソッド: 外部に公開する操作(API)です。例えば、オブジェクトの状態を取得するゲッターや、状態を設定するセッターなどが該当します。
  • privateメソッド: クラス内部の複雑な処理を分割するためのヘルパーメソッドなど、外部に公開する必要がない処理に使われます。リファクタリングの際に、長大なpublicメソッドを複数のprivateメソッドに分割することはよくあります。

フィールド(変数)にアクセス修飾子をつけるときの注意点

繰り返しになりますが、フィールド(インスタンス変数)には原則としてprivate修飾子を付けます。これは、カプセル化(情報隠蔽)の原則を守るためです。

もしフィールドをpublicにしてしまうと、どこからでも値を直接書き換えられるようになり、オブジェクトの状態が予期せぬ形で変更されてしまう危険性があります。

値の取得や設定が必要な場合は、publicなゲッターメソッドやセッターメソッドを用意しましょう。セッターメソッド内で値の妥当性チェック(例えば、年齢に負の値が設定されないようにするなど)を実装することで、オブジェクトの状態を常に正しく保てます。

アクセス修飾子を使い分けるときの判断基準

どのアクセス修飾子を選べばよいか迷ったときは、「可能な限りアクセス範囲を狭くする」という原則に立ち返りましょう。これを最小権限の原則と呼びます。

  1. 最初にprivateで定義できないか検討する。
  2. 同一パッケージ内の他クラスからのアクセスが必要な場合はdefaultにする。
  3. 継承したサブクラスに公開する必要がある場合はprotectedにする。
  4. 上記以外で、どうしても外部に公開する必要がある場合にのみpublicを選択する。

この思考プロセスを習慣づけることで、自然と堅牢で安全なクラス設計ができるようになります。

アクセス修飾子を理解するためのコード例

ここでは、具体的なコードを見ながらアクセス修飾子の動作を確認していきましょう。

public と private を組み合わせたサンプルコード

ゲッター/セッターは、publicprivateの役割を理解するための最も典型的な例です。

public class User {
    // フィールドはprivateで外部から隠蔽する
    private String name;
    private int age;

    public User(String name, int age) {
        this.name = name;
        // セッターを経由して初期値を設定する
        this.setAge(age);
    }

    // publicなゲッターで値を取得する
    public String getName() {
        return this.name;
    }

    public int getAge() {
        return this.age;
    }

    // publicなセッターで値を設定する
    // 不正な値が設定されないようにチェックできる
    public void setAge(int age) {
        if (age >= 0) {
            this.age = age;
        } else {
            System.out.println("年齢に負の値は設定できません。");
        }
    }
}

このコードでは、nameageフィールドをprivateにすることで、直接のアクセスを防いでいます。年齢ageに負の値のような不正なデータがセットされないよう、setAgeメソッド内でチェック処理を入れている点がポイントです。

protected と default の動作を比較する例

protecteddefaultの違いは、パッケージをまたいだ継承関係で顕著になります。

パッケージ1: com.example.animal

package com.example.animal;

public class Animal {
    // 同一パッケージとサブクラスからアクセス可能
    protected String protectedSound = "鳴き声";
    
    // 同一パッケージからのみアクセス可能
    String defaultName = "動物";

    public void cry() {
        System.out.println(this.protectedSound);
    }
}

パッケージ2: com.example.main

package com.example.main;

import com.example.animal.Animal;

// Animalを継承したサブクラス
public class Dog extends Animal {
    public void accessTest() {
        // protectedメンバーには別パッケージのサブクラスからアクセスできる
        System.out.println(this.protectedSound); // OK

        // defaultメンバーには別パッケージからはアクセスできない
        // System.out.println(this.defaultName); // コンパイルエラー!
    }
}

DogクラスはAnimalクラスを継承しています。別パッケージにありますが、サブクラスなのでprotectedprotectedSoundフィールドにアクセスできます。一方、defaultdefaultNameフィールドにはアクセスできず、コンパイルエラーが発生します。

アクセス制限エラーの原因と対処法

Javaでプログラミングをしていると、以下のようなコンパイルエラーに遭遇することがあります。

The field [フィールド名] is not visible

[メソッド名] has private access in [クラス名]

これらのエラーは、アクセスが許可されていないメンバー(フィールドやメソッド)にアクセスしようとしたときに発生します。

原因:

アクセスしようとしているメンバーのアクセス修飾子によって、アクセスが制限されている。

対処法:

  1. 設計を見直す: なぜそのメンバーにアクセスする必要があるのかを考えます。privateなメンバーに直接アクセスしようとしている場合、それはクラスの設計思想に反している可能性があります。代わりに用意されているpublicなメソッドを使うべきではないか検討しましょう。
  2. アクセス修飾子を変更する: 設計上、どうしてもアクセスが必要な場合は、アクセス修飾子をより緩いもの(例: private -> defaultpublic)に変更します。ただし、これは安易に行うべきではなく、なぜそのアクセスレベルが必要なのかを明確に説明できる場合に限ります。

アクセス修飾子を正しく使うためのベストプラクティス

最後に、アクセス修飾子を効果的に活用し、より質の高いコードを書くためのベストプラクティスを紹介します。

最小限のアクセス権にする原則(情報隠蔽の重要性)

繰り返し強調しますが、常に最小限のアクセス権を付与することを心がけてください。これは「情報隠蔽」や「最小権限の原則」として知られる、オブジェクト指向設計の基本中の基本です。

  • 変更の影響範囲を限定できる: 内部実装をprivateで隠蔽しておけば、将来その実装方法を変更しても、クラスの外部(利用者側)に影響が及びません。
  • クラスの利用者が迷わない: publicなメソッドだけを見れば、そのクラスの使い方が分かります。内部の複雑な実装を気にする必要がなくなり、利用しやすさが向上します。
  • バグの混入を防ぐ: 意図しない使われ方を防ぎ、オブジェクトの状態を健全に保つことで、デバッグが容易で安定したプログラムになります。

チーム開発でのアクセス修飾子の設計ルール

チームで開発を行う場合、アクセス修飾子の使い方は、個人の裁量だけでなく、チームとしてのコーディング規約で定めることが望ましいです。

  • APIとして公開する範囲を明確にする: どのクラスやメソッドをpublicにするか(つまり、チーム内の他のメンバーが使ってよい機能か)を設計段階で合意します。
  • パッケージの設計: 関連性の高いクラスを同じパッケージにまとめ、defaultアクセス修飾子を有効に活用することで、パッケージ内での連携を密にしつつ、パッケージ外への不要な情報流出を防げます。

明確なルールを設けることで、コードの意図が伝わりやすくなり、開発効率とメンテナンス性が向上します。

よくある誤用パターンとその修正例

誤用パターン1: とりあえず全部publicにしてしまう

初心者によく見られるパターンです。アクセスエラーが出るたびにpublicに変更していくと、最終的にすべてのメンバーがpublicになってしまい、カプセル化が完全に崩壊します。

  • 修正例: フィールドは必ずprivateにします。メソッドもまずはprivateで考え、外部に公開する必要があるものだけをpublicに変更していく、という手順を踏みましょう。

誤用パターン2: protectedの乱用

継承が便利だからといって、安易にprotectedを使うべきではありません。protectedはクラス間の結合度を強め、サブクラスが親クラスの実装に強く依存してしまう原因になります。

  • 修正例: 継承は強力ですが、結合度が高い諸刃の剣です。本当に継承が必要な設計なのか、委譲(コンポジション)で代替できないかを検討しましょう。protectedは、フレームワークのように拡張を前提とした設計など、明確な意図がある場合に限定して使用するのが賢明です。

まとめ|アクセス修飾子を理解して安全なコードを書く

この記事では、アクセス修飾子の基本から実践的な使い方までを解説してきました。最後に、重要なポイントを再確認しましょう。

4つのアクセス修飾子の使い分けを再確認

  • public: 誰でもアクセスOK。外部に公開するAPI。
  • protected: パッケージ内 + 別パッケージのサブクラス。継承が前提。
  • default: パッケージ内のみ。パッケージの部品。
  • private: クラス内のみ。クラスの内部実装。原則これを使う。

迷ったときは、最も厳しいprivateから検討し、必要に応じて少しずつアクセス範囲を広げていくのが鉄則です。

アクセス修飾子を意識することが設計力の第一歩

アクセス修飾子を適切に使い分けることは、単なる文法ルールの話ではありません。それは、クラスの責務を考え、いかにして安全で変更に強い部品を作るかという、ソフトウェア設計そのものに深く関わっています。

「この情報はどこまで見せるべきか?」と常に自問自答する習慣が、あなたのコードを一段上のレベルへと引き上げてくれるでしょう。ぜひ、今日からのコーディングで意識してみてください。

  • この記事を書いた人
  • 最新記事

トム

・都内自社開発IT企業勤務/javaのバックエンドエンジニア
/java歴10年以上 ・首都圏在住30代
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎

-Java入門