2009-03-19 12 views
26

これがなぜコンパイルされるのか分かりません。 f()とg()はプライベートであるにもかかわらず、内部クラスから可視です。彼らは内部クラスであるため特別扱われていますか?なぜ内部クラスでプライベートメソッドにアクセスできるのですか?

AとBが静的クラスでない場合でも、それは同じです。

class NotPrivate { 
    private static class A { 
     private void f() { 
      new B().g(); 
     } 
    } 

    private static class B { 
     private void g() { 
      new A().f(); 
     } 
    } 
} 

答えて

26

(編集:コメントのいくつかに答えることを答えに拡大)

は、コンパイラが内部クラスを取り、トップレベルのクラスにそれらを回します。プライベートメソッドはインナークラスでのみ使用可能であるため、コンパイラはパッケージレベルのアクセス権を持つ新しい「合成」メソッドを追加して、トップレベルのクラスがアクセスできるようにする必要があります。このような

何か($ものは、コンパイラによって追加されます):

class A 
{ 
    private void f() 
    { 
     final B b; 

     b = new B(); 

     // call changed by the compiler 
     b.$g(); 
    } 

    // method generated by the compiler - visible by classes in the same package 
    void $f() 
    { 
     f(); 
    } 
} 

class B 
{ 
    private void g() 
    { 
     final A a; 

     a = new A(); 

     // call changed by the compiler 
     a.$f(); 
    } 

    // method generated by the compiler - visible by classes in the same package 
    void $g() 
    { 
     g(); 
    } 
} 

非静的クラスは同じですが、方法ができるように、彼らは外側のクラスへの参照の追加を持っていますそれに呼び出される。

Javaがこのようにする理由は、内部クラスをサポートするためにVMの変更を必要としたくないため、すべての変更がコンパイラレベルでなければならなかったからです。

コンパイラは内部クラスを取り、それをトップレベルクラスに変換します(したがって、VMレベルでは内部クラスのようなものはありません)。コンパイラはまた、新しい "転送"メソッドを生成する必要があります。これらは、パッケージレベル(公開されていない)で作成され、同じパッケージ内のクラスのみがアクセスできるようにします。コンパイラはまた、生成された "転送"メソッドに対するプライベートメソッドへのメソッド呼び出しを更新しました。

コンパイラがメソッドを「パッケージ」(public、private、およびprotectedの不在)として宣言しているのを避けることができます。その欠点は、パッケージ内のどのクラスでもメソッドを呼び出せることです。

+1

そのため、メソッドをprivateに宣言し、コンパイラはエラーを生成せずにpublicにしますか? – cbrulak

+0

まあパブリックではありませんパッケージです。そして、はい、そうです。これは、VMに内部クラスに関する知識がないためです。コンパイラは、VMを特別な処理を行う代わりに、すべての作業を行います。 – TofuBeer

+0

パッケージは内部クラスを含むクラスですか? – cbrulak

3

Javaは$を含む特別なアクセサでコンパイルします。したがって、プライベートメソッドにアクセスするJavaを記述することはできません。ここで説明:

http://www.retrologic.com/innerclasses.doc7.html

をコンパイラが生成したメンバーの1つの以上のカテゴリがあります。クラスCのプライベートメンバーmは、あるクラスが他のクラスを囲む場合、またはそれらが共通クラスで囲まれている場合、別のクラスDによって使用されることがあります。仮想マシンはこの種のグループ化について知らないので、コンパイラはCでアクセスメソッドのローカルプロトコルを作成し、Dがメンバmを読み書きしたり呼び出すことを許可します。これらのメソッドは、アクセス$ 0、アクセス$ 1などの形式の名前を持ちます。それらは決して公開されません。アクセスメソッドは内部クラスだけでなく、囲むクラスに追加できるという点でユニークです。

13

私はthis quoteはうまくそれを合計思う:

...内部クラスでもプライベートメンバー、宣言クラスのすべてのメンバーにアクセスすることができます。実際、内部クラス自体はクラスのメンバーであると言われています。したがって、オブジェクト指向のエンジニアリングのルールに従って、クラスのすべてのメンバーにアクセスできる必要があります。

それから、両方の内部クラスは実際には包含クラスの一部であるため、他のプライベートメンバーにもアクセスできる必要があります。

+0

"実際には、クラスがコンパイルされるときに、すべての内部クラスが実際に '包含されたクラスの一部になるために'折りたたまれます。 - これはどういう意味ですか? –

+0

私はそれが本当でないと確信しています。通常、Outer $ Innerという形式のクラスが作成されます。内クラスと外クラスのオブジェクトの数が異なるため、あなたが言ったことを本当に行うことはできません。 –

+0

あなたは正しいです、それは少し誤解を招いていました。私の答えからその部分を削除しました。 –

0

これは仕様で動作する必要があります。 Java言語仕様はそう言う:メンバーまたはコンストラクタが、その後のアクセスがあれば許可され、それがの本体内で発生した場合にのみされ、プライベート宣言されている場合

6.6.1は、そうでなければ」

(少なくともJLS6以降)のアクセシビリティを決定しますメンバーまたはコンストラクタの宣言を囲むトップレベルのクラス(§7.6)。

I.e.プライベートメンバーの「アクセススコープ」は、トップクラスのクラス本体のレキシカル境界内のどこにでもあります。

つまり、最も外側のクラスのclass-body内で定義されているすべてのプライベートメンバーには、このclass-body内の他の場所からアクセスできます。

例えば、内部クラスのプライベートメソッドは、外部クラスのメソッドから、または外部クラスの別の内部クラスのメソッドからアクセスできます。

関連する問題