private 修飾子

Revised: May/4th/2008; Since: Dec./21st/2003

private

アクセス修飾子の中で最もきついのが private です。クラスの private 修飾子が付されたメンバーは、当該クラス内からしかアクセスできません。内部クラスからは参照可能であり、同じクラスからインスタンス化されたオブジェクトからは相互に参照できます。

アクセス修飾子は、制限を弱める方向でオーバーライド可能です。例外として、private 修飾されたフィールドはオーバーライド不可なのですが、実際には同じシグネチャ(メソッド名と引数の組)のメソッドをサブクラスで定義可能です。サブクラスにはprivate修飾されたメンバは継承されない(サブクラスからは見えない)ので、全く別のメソッドとして実装可能ということです。

final

final は、継承によってオーバーライドできないメンバーを宣言する修飾子です。private なメンバーは、自動的に final です。同じクラス内からのみアクセスが許されているので、継承するサブクラスからもアクセスできません。したがって、言語仕様上、継承によりオーバーライドすることはできず、本質的に final となるのです。

Java では、継承でオーバーライドできるメンバーは、アクセス可能なものに限られ、private なメンバーは、外部の一切のクラスからアクセス不可能なのでオーバーライドできないのです。

言語仕様として、private なメンバーはオーバーライドできないことを覚えてください。

オーバーライドと隠蔽と private

オーバーライドとは、「スーパークラスで定義されているメソッドと、同じメソッド名と同じシグネチャを持つメソッドをサブクラスで定義する」ことです。このとき、次の条件の何れかを満たさなければコンパイルエラーになります。

static 修飾されているメソッドに関しては、更に次の条件が与えられます。

スーパークラスの private メソッドをサブクラスで再定義するとき、オーバーライドや隠蔽 (hide) の条件を満たす必要が無く、完全に自由にメソッドを再定義可能です。

インライン化

privatestatic なメソッドは、インライン化される可能性があります。インライン化とは、オブジェクトのメソッドへの参照を持つ代わりに、当該メソッドそのものをメモリの連続領域に展開することです。ポインタを経由することを省くので、パフォーマンスが向上します。但し、JVM が実行時にそうしても良いというだけなので、必ずインライン化されるわけではありません。実際の挙動はJVMの実装に依存します。

それにも関わらず、JVMによらず(少なくとも殆どのフルJVMで)static修飾された定数はインライン化されます。このことは、static定数が定義されたクラス(もしくはインタフェース)と、それを参照するクラスで、別の場所にリテラルが書き込まれていることを意味します。仮に、static定数の定義元を更新してコンパイルしても、それを参照するクラスをリコンパイルしなければ、実行中に参照される値は変更されません。

この動作は非常に危険なので、リリース前のビルド時には、全量再コンパイルをお勧めすると共に、変更される可能性の高い値は、*.propertiesファイルやXMLファイルへ書くようにお勧めします。

J2SE 5.0 (Tiger)以上では、staticインポートという言語仕様が追加されました。staticメンバーをimport文で宣言することで、クラス名修飾が不要になるというものです。関係ありませんが、ここでも紹介しておきます。

import static System.out;
import static System.err;

class TestStaticImport {
	public static void main(String[] args) {
		out.println("標準出力");
		err.println("標準エラー出力");
	}
}

メンバーの private 修飾

フィールドの private

static以外のメンバー変数(フィールド)は、原則として全て 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 なメンバーはオーバーライド不可です。しかし、サブクラスで同じシグネチャ(メソッド名と引数の組)のメソッドを定義することが可能で、現象的にはオーバーライドと同じですが、まったく新しいメソッドを別途定義していることになるため、オーバーライドの制約を一切受けません。

利用クラス(クライアント・コード)が自クラス以外に存在しないので、自由に更新することが可能です。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());
	}
}

コンストラクタの private

コンストラクタは、クラスがインスタンス化されるときに必ず呼び出されるもので、クラスのメンバーではありません。コンストラクタはインスタンス化される前に呼び出されるので、自動的に 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 で唯一つだけに制限されます。このデザインパターンをシングルトン・パターンと呼びます。初期化ロジックが複雑なインスタンスの生成を実装するファクトリー・メソッドと組み合わせて利用することが一般的です。



Copyright © 2003-2008 SUGAI, Manabu. All Rights Reserved.