概要
こちらの投稿を読んで、同じクラスであれば他インスタンスの private フィールドにアクセスできてしまうのは何故か?について気になったので考察してみました。
具体例
class Main {
public void static main(String[] args) {
Something obj1 = new Something("foo");
Something obj2 = new Something("bar");
// bar と出力される
System.out.println(obj1.extractName(obj2));
}
}
class Something {
private String name;
/**
* コンストラクタ
*/
public Something(String name) {
this.name = name;
}
/**
* 引数に渡されたオブジェクトの private なフィールドを取得する
*/
public String extractName(Something other) {
return other.name; // ここで他のインスタンスの private フィールドにアクセスできてしまうことに違和感がある
}
}
本質的には Getter で private フィールドを返すのと同じことなんじゃないだろうか?
一般的な Getter は以下のように定義しますが、
class Something {
private String name;
public Something(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
ちょっと書き方を変えてみると次のようにも書けます。
※ this を他の変数に格納する、ということはあまりやらないと思いますが問題なく動きます
public String getName() {
Something instance = this;
return instance.name;
}
これは前出の Something#extractName メソッドでやっていることと本質的には変わらない。
どちらも、Something 型のオブジェクトの private フィールドである name にアクセスし、値を返している。
private フィールドへのアクセスに関する誤解
これまで「private で宣言されたフィールドには this を通して、そのインスタンス自身からしかアクセスできない」というような認識でしたがおそらくそれは正確ではなく、次のように考えれば統一的に理解できます。
- 大前提として、フィールドには
instance.fieldName
という形でアクセスする - public なフィールドはプログラムのどこでも
instance.fieldName
という形でアクセス可能 - private なフィールドはそのクラス定義内であれば
instance.fieldName
という形でアクセス可能 - this はそのインスタンス自身の参照が入っているだけで他の変数と変わらない
つまり、普段 private フィールドへのアクセスが this.name
のように記述できているのは、「そのインスタンスのフィールドだから」ではなく、「Something クラス内では (Somethingクラスのインスタンス).name
という形でのprivateフィールドへのアクセスが許可されているから」である、ということのようです。
メソッドの場合
試してみたところメソッドでも同じです。
他インスタンスの private メソッドを呼び出せています。
public class Main {
public static void main(String[] args) throws Exception {
Something obj1 = new Something("foo");
Something obj2 = new Something("bar");
// obj2 内の private メソッドが呼び出され、
// My name is bar と出力される
obj1.test(obj2);
}
}
class Something {
private String name;
public Something(String name) {
this.name = name;
}
private void privateMethod() {
System.out.println("My name is " + this.name);
}
public void test(Something other) {
other.privateMethod();
}
}