あなたのコードに誰が触れられるか
アクセス修飾子とは、クラス、フィールド、メソッド、コンストラクタの前に置くキーワードで、ほかに誰がそれを使ってよいかを決めるものです。これはカプセル化の土台です。クラスのごちゃごちゃした内部を隠し、ほかのコードに呼んでもらってよいと信頼できる部分だけを公開します。
Javaには、最も開かれたものから最も閉ざされたものまで4つのレベルがあります。public、protected、default(キーワードがまったくない状態)、privateです。これらを正しく選べば、クラスはあとから安全に変更できるようになります。外部の何ものも、維持すると約束したわけではない詳細に依存しなくなるからです。
private: クラスの中だけで見える
privateは最も厳しいレベルです。privateなメンバーには、同じクラスの中に書かれたコードだけが触れられます。サブクラスでも、同じパッケージのほかのクラスでも、誰も触れられません。あなたのフィールドの大半が属するのはここです。
balanceはprivateなので、それを変える唯一の方法は、負の金額を拒否するdepositを通すことです。もしbalanceがpublicだったら、どんなコードでもaccount.balance = -9999と書いてチェックを完全に飛ばせてしまいます。そのガードこそが、フィールドを隠すことの目的そのものです。
mainの中のaccount.balance = 500;のコメントを外してみてください。balance has private access in BankAccountというコンパイルエラーが出ます。
public: どこからでも見える
publicはその正反対の極です。どこにいる誰でもそのメンバーを使えます。ほかのコードに呼んでもらうことを意図したメソッド名は、たいていpublicです。ほかのパッケージから到達できる必要があるとき、クラスそのものもpublicになります。
よくあるパターン - そのクラスの公開APIと呼ばれることもあります - は、privateなフィールド、publicなメソッドです。フィールドは、ほかの誰にもいじらせるべきでない状態を保持します。メソッドは、出入りのための制御された扉です。上のgetBalanceとdepositがまさにこれです。
default(package-private): キーワードがまったくない
修飾子を何も書かなければ、defaultアクセス、別名package-privateになります。そのメンバーは同じパッケージのすべてのクラスから見え、その外側のすべてからは見えません。このレベルにはキーワードがありません。キーワードがないことそのものがレベルなのです。
class Invoice { // 修飾子なし -> このパッケージ内だけで見える
int amount; // 修飾子なし -> package-private なフィールド
}
これは多くの初心者がはまります。publicを省いても、それはprivateにはならず、package-privateになります。あなたが秘密にしておくつもりだったフィールドは、たまたま同じパッケージにいるどのクラスからでも、依然として完全に読み書きできます。本当に隠したいなら、privateを明示的に書かなければなりません。
defaultアクセスは、あるパッケージの実装の詳細であるヘルパークラスにとって本当に役立ちます。エクスポートはしませんが、パッケージ内で協調するクラスは自由にそれらを使えます。
protected: パッケージに加えてサブクラス
protectedはdefaultより一段だけ緩いものです。protectedなメンバーは、defaultが見える場所(同じパッケージ)すべてで見え、さらにサブクラスからも見えます。別のパッケージのサブクラスからでもです。自分のクラスを継承するクラスとは共有したいが、世界全体とは共有したくないもののために設計されています。
Dogがnameとsound()にアクセスできるのは、それらがprotectedであり、Dogがサブクラスだからです。サブクラスでもなくパッケージにもいない外部のコードは、依然としてそれらに触れられません。継承されることを意図したクラスを設計しているときはprotectedに手を伸ばしましょう。そうでなければprivateを選びましょう。
4つのレベルを一目で
最も制限的なものから最も開かれたものまで、全体像はこうです。
| 修飾子 | 同じクラス | 同じパッケージ | サブクラス(別パッケージ) | どこでも |
|---|---|---|---|---|
private | はい | いいえ | いいえ | いいえ |
| default | はい | はい | いいえ | いいえ |
protected | はい | はい | はい | いいえ |
public | はい | はい | はい | はい |
簡単な経験則はこうです。privateから始め、何かが本当に広いアクセスを必要とするときだけ緩めること。 ほかのコードがあるメンバーに依存し始めたあとにアクセスを狭めるより、あとから広げるほうがずっと簡単です。
クラス自体に付ける修飾子
アクセス修飾子はメンバーだけでなくクラスにも適用されます。ただしトップレベルのクラス(別のクラスの中に入れ子になっていないもの)は、publicかdefaultのどちらかにしかできません。トップレベルのprivateやprotectedなクラスは書けません。
public class Order { ... } // どのパッケージからも到達できる
class LineItem { ... } // default - このパッケージだけが見える
publicなトップレベルのクラスは、それにちなんだ名前のファイル(Order.java)に置かなければなりません。一方、入れ子のクラスは4つの修飾子すべてを使えます。だからこそ上の例では、内部クラスをprivateやdefaultにできたのです。微妙な点が1つあります。package-privateな型を返すpublicメソッドは、外部の呼び出し側にはあまり役立たないので、公開する面を一貫させましょう。
次へ: 静的メンバー
ここまで、すべてのフィールドとメソッドはインスタンスに属していました。newでオブジェクトを作り、それに対してメソッドを呼び出します。しかし、値や振る舞いがクラス全体に属し、すべてのインスタンスで共有されることもあります。たとえば、オブジェクトがいくつ存在するかを数えるカウンタのようなものです。それこそがstaticキーワードの役目であり、次はそれを取り上げます。
よくある質問
Javaの4つのアクセス修飾子とは何ですか?
public、private、protected、そしてdefault(キーワードなし、package-privateとも呼ばれる)です。publicはどこからでも見え、privateは同じクラスの中だけ、protectedはパッケージ内に加えてサブクラスから、defaultは同じパッケージの中だけで見えます。
Javaでpublicとprivateの違いは何ですか?
publicは、どこにあるどんなコードでもそのメンバーにアクセスできることを意味します。privateは、同じクラスの中のコードだけがアクセスでき、サブクラスやほかのクラスからはそもそも見えないことを意味します。通常はフィールドをprivateにし、publicメソッドを通じて公開します。
Javaのフィールドにアクセス修飾子がないと何を意味しますか?
キーワードがないことはdefault(package-private)アクセスを意味します。そのメンバーは同じパッケージのすべてのクラスから見えますが、その外からは見えません。publicより厳しく、privateより緩いものです。うっかり書き忘れやすいので、意識して扱いましょう。