外部クラスが内部クラスのプライベートインスタンス変数にアクセスできることを確認しました。これはどのように可能ですか?同じことを示すサンプルコードを次に示します。
class ABC{
class XYZ{
private int x=10;
}
public static void main(String... args){
ABC.XYZ xx = new ABC().new XYZ();
System.out.println("Hello :: "+xx.x); ///Why is this allowed??
}
}
なぜこの動作が許可されているのですか?
内部クラスは、元の外部クラスに実際に属するいくつかの機能をきれいに分離するための単なる方法です。次の2つの要件がある場合に使用することを目的としています。
これらの要件を考慮すると、内部クラスは外部クラスに完全にアクセスできます。基本的に外部クラスのメンバーであるため、プライベートを含む外部クラスのメソッドと属性にアクセスできることは理にかなっています。
内部クラスのプライベートメンバーを非表示にする場合は、パブリックメンバーでインターフェイスを定義し、このインターフェイスを実装する匿名の内部クラスを作成できます。以下の例:
class ABC{
private interface MyInterface{
void printInt();
}
private static MyInterface mMember = new MyInterface(){
private int x=10;
public void printInt(){
System.out.println(String.valueOf(x));
}
};
public static void main(String... args){
System.out.println("Hello :: "+mMember.x); ///not allowed
mMember.printInt(); // allowed
}
}
内部クラスは(アクセス制御の目的で)包含クラスの一部と見なされます。これは、すべてのプライベートへのフルアクセスを意味します。
これを実装する方法は、パッケージで保護された合成メソッドを使用することです。内部クラスは、同じパッケージ(ABC $ XYZ)内の別のクラスにコンパイルされます。 JVMはこのレベルの分離を直接サポートしていないため、バイトコードレベルでABC $ XYZには、プライベートメソッド/フィールドに到達するために外部クラスが使用するパッケージ保護されたメソッドがあります。
これに似た別の質問に正しい答えが表示されます: なぜ、ネストしたクラスのプライベートメンバーに、囲んでいるクラスのメソッドからアクセスできるのですか?
JLS-アクセシビリティの決定 にはプライベートスコープの定義があると書かれています。
そうでない場合、メンバーまたはコンストラクターがprivateとして宣言されている場合、、メンバーの宣言を囲むトップレベルクラス(§7.6)の本体内で発生する場合にのみアクセスが許可されます。コンストラクター
内部クラスのIMHOの重要な使用例は、ファクトリパターンです。エンクロージングクラスは、アクセス制限なしで内部クラスのインスタンスを準備し、そのインスタンスをプライベートアクセスが許可される外部の世界に渡すことができます。
abyx に反して、クラスstaticを宣言しても、次に示すように、包含するクラスへのアクセス制限は変更されません。また、同じ包含クラス内の静的クラス間のアクセス制限が機能しています。私はびっくりしました ...
class MyPrivates {
static class Inner1 { private int test1 = 2; }
static class Inner2 { private int test2 = new Inner1().test1; }
public static void main(String[] args) {
System.out.println("Inner : "+new Inner2().test2);
}
}
アクセス制限はクラスごとに行われます。クラスで宣言されたメソッドが、すべてのインスタンス/クラスメンバーにアクセスできないようにする方法はありません。これは、内部クラスにも外部クラスのメンバーへの無制限のアクセス権があり、外部クラスには内部クラスのメンバーへの無制限のアクセス権があるためです。
クラスを別のクラス内に配置することにより、実装に密接に結び付けられ、実装の一部であるものはすべて、他の部分にアクセスできるようになります。
内部クラスの背後にあるロジックは、外部クラスで内部クラスを作成する場合、いくつかのことを共有する必要があるため、「通常の」クラスよりも柔軟性を持たせることが理にかなっているということです。
あなたの場合、クラスがお互いの内部動作を見ることができない場合-基本的に内部クラスを単純に通常のクラスにした可能性があることを意味する場合、内部クラスをstatic class XYZ
として宣言できます。 static
を使用すると、状態を共有しなくなります(たとえば、new ABC().new XYZ()
は機能しません。new ABC.XYZ()
を使用する必要があります。
しかし、もしそうなら、XYZ
が本当に内部クラスであるべきかどうか、そしておそらくそれが独自のファイルに値するかどうかを考える必要があります。静的な内部クラスを作成することが理にかなっている場合があります(たとえば、外部クラスが使用しているインターフェイスを実装する小さなクラスが必要で、それ以外の場所では役に立たない場合)。しかし、約半分の時間で外部クラスにすべきでした。
Thiloは、最初の質問「これはどのように可能ですか?」に answer を追加しました。 2番目の質問について少し詳しく説明したいと思います。この動作が許可されているのはなぜですか。
まず第一に、この振る舞いは、定義により非静的なネストされたタイプである内部クラスにのみ許可されないことを完全に明確にしましょう。この動作は、静的である必要があり、囲むインスタンスを持つことができないネストされた列挙型およびインターフェイスを含む、すべてのネストされた型で許可されます。基本的に、モデルは次のステートメントに至るまで簡略化されています。ネストされたコードは、囲んでいるコードに完全にアクセスできます。逆も同様です。
それでは、なぜですか?例がポイントをよりよく示していると思います。
あなたの体と脳について考えてください。ヘロインを腕に注入すると、脳が高くなります。あなたの脳の扁桃体領域が、彼があなたの個人的な安全への脅威であると信じているもの、たとえばスズメバチを見ると、彼はあなたの体を逆向きにし、あなたがそれについて二度「考え」ないで丘に向かって走ります。
したがって、脳は身体の本質的な部分であり、奇妙なことに、逆もまた同様です。このような密接に関連するエンティティ間でアクセス制御を使用すると、関係の主張が失われます。アクセス制御が必要な場合は、クラスをさらに明確なユニットにさらに分ける必要があります。それまでは、同じユニットです。さらなる研究の推進例は、Java Iterator
が通常どのように実装されるかを調べることです。
囲んでいるコードからネストされたコードへの無制限のアクセスは、ほとんどの場合、ネストされたタイプのフィールドとメソッドにアクセス修飾子を追加するのではなく、役に立たないものにします。そうすることは煩雑さを追加することであり、Javaプログラミング言語の新参者に誤った安全感を与える可能性があります。