Revised: Jan./3rd/2004; Since: Dec./21st/2003
private
アクセス修飾子の中で最もきついのが private
です。クラスの private 修飾子が付されたメンバーは、当該クラス内からしかアクセスできません。内部クラスからは参照可能であり、同じクラスからインスタンス化されたオブジェクトからは相互に参照できます。
アクセス修飾子は、制限を弱める方向でオーバーライド可能です。例外として、private 修飾されたフィールドはオーバーライド不可なのですが、現実的には同じシグネチャ(メソッド名と引数の組)のメソッドをサブクラスで定義可能です。
これがいったいどういうことなのか以下で説明します。
final
final
は、継承によってオーバーライドできないメンバーを宣言する修飾子です。private
なメンバーは、自動的に final
です。同じクラス内からのみアクセスが許されているので、継承するサブクラスからもアクセスできません。したがって、言語仕様上、継承によりオーバーライドすることはできず、本質的に final
となるのです。
Java では、継承でオーバーライドできるメンバーは、アクセス可能なものに限られ、private なメンバーは、外部の一切のクラスからアクセス不可能なのでオーバーライドできないのです。
言語仕様として、private なメンバーはオーバーライドできないことを覚えてください。
private
private
なメンバーはオーバーライドされせん。同一クラス内からしかアクセスできないので、サブクラスで継承するオーバーライドの概念にあわないからです。しかし、アクセスできないため、まったく新たなメンバーとして定義可能です。
オーバーライドとは、「スーパークラスで定義されているメソッドと、同じメソッド名と同じシグネチャを持つメソッドをサブクラスで定義する」ことです。このとき、次の条件の何れかを満たさなければコンパイルエラーになります。
static
修飾されているメソッドに関しては、更に次の条件が与えられます。
static
メソッドをサブクラスの static
メソッドで再定義することを隠蔽と呼ぶstatic
メソッドがインスタンスメソッドを隠蔽するとコンパイルエラーとなるstatic
メソッドを隠蔽するとコンパイルエラーとなるスーパークラスの private
メソッドをサブクラスで再定義するとき、オーバーライドや隠蔽 (hide) の条件を満たす必要が無く、完全に自由にメソッドを再定義可能です。
private
や static
なメソッドは、インライン化される可能性があります。インライン化とは、オブジェクトのメソッドへの参照を持つ代わりに、当該メソッドそのものをメモリの連続領域に展開することです。ポインタを経由することを省くので、パフォーマンスが向上します。但し、JVM が実行時にそうしても良いというだけなので、必ずインライン化されるわけではありません。インライン化するだけのメモリの余裕がある場合に、そのほかのリソースの使用率と相談した上で、 JVM が動作を変更します。
private
修飾private
メンバー変数であるフィールドは、原則として全て private
にします。
クラス外部からのアクセスは、アクセス用のメソッドを公開します。このメソッドをアクセッサー(ゲッターとセッター)と呼びます。
PrivateFieldDemo.java
:
class PrivateField { private String name; PrivateField(String aName) { name = aName; } public String getName() { return name; } public void setName(String aName) { // 同じクラスだからアクセス可能 name = aName; } public void setName(PrivateField obj, String aName) { // 別のオブジェクトでも同じクラスだからアクセス可能 obj.name = aName; } } class PrivateFieldDemo { public static void main(String[] args) { PrivateField obj1 = new PrivateField("suzuki"); PrivateField obj2 = new PrivateField("tochihara"); System.out.println("obj1.name(): " + obj1.getName()); System.out.println("obj2.name(): " + obj2.getName()); obj1.setName("hiroe"); obj1.setName(obj2, "sekiya"); System.out.println("obj1.name(): " + obj1.getName()); System.out.println("obj2.name(): " + obj2.getName()); } }
実行結果:
C:\java>javac PrivateFieldDemo.java C:\java>java PrivateFieldDemo obj1.name(): suzuki obj2.name(): tochihara obj1.name(): hiroe obj2.name(): sekiya C:\java>
private
private なメンバーはオーバーライド不可です。しかし、サブクラスで同じシグネチャ(メソッド名と引数の組)のメソッドを定義することが可能で、現象的にはオーバーライドと同じですが、まったく新しいメソッドを別途定義していることになるため、オーバーライドの制約を一切受けません。
PrivateOverrideDemo.java
:
class PrivateSuper { private String msg = "Bye"; private String getPrivateMsg() { return msg; } } class PrivateOverride extends PrivateSuper{ private String msg = "Hello"; private String getPrivateMsg() { // private メソッドのオーバーライドのように見える // String str = super.msg; // javac Error // String str = super.getPrivateMsg(); // javac Error String str = msg; return str; } public String getMsg() { return this.getPrivateMsg(); } } class PrivateOverrideDemo { public static void main(String[] rags) { PrivateOverride obj = new PrivateOverride(); // System.out.println(obj.getPrivateMsg()); // javac Error System.out.println(obj.getMsg()); } }
コンストラクタは、クラスがインスタンス化されるときに必ず呼び出されるもので、クラスのメンバーでは在りません。コンストラクタはインスタンス化される前に呼び出されるので、自動的に static です。
コンストラクタを private 修飾する場合を考えましょう。private は他のクラスからは一切アクセス不能なので、private コンストラクタしか持たないクラスはインスタンス化不能に思われます。しかし、そうではないのです。
コンストラクタを private にするメリットは、他のコードが勝手にインスタンス化したオブジェクトを生成できなくすることです。このオブジェクトを取得する仕組みとして、ファクトリメソッドを用意することが考えられます。具体的には、private static
なフィールドに自分のインスタンスを用意して、getter メソッドを介して取得します。
class SingletonDemo { private static SingletonDemo obj = new SingletonDemo(); private SingletonDemo() { // 適当な初期化 } public Singleton getSingletonInstance() { return obj; } }
フィールド(クラスのメンバーである変数)に定義されたオブジェクトは、static 宣言されているので、クラスのロード時にただ一回だけ実行されます。従って、クラスが何度参照されようとも、ただ一回だけ初期化され、生成されるインスタンスも、一つの JVM で唯一つだけに制限されます。