2009-07-06 7 views
10

は、次のような場合を考えてみたJavaコンパイラは、合成アクセサメソッドによってコンストラクタA.B()をエミュレートします。私は今コンパイラがBのための余分な "水中"コンストラクタを作成すると思います。Javaの内部クラスの可視性のパズル

私はこれがむしろ奇妙だと感じます:なぜクラスBはa.k.oとして見えませんか? Aのフィールド? そして、クラスBは実行時にプライベートではなくなりましたか? そして、なぜクラスBのprotectedキーワードが異なるのですか?

public class A { 
    public A() { b = new B(); } 
    B b; 
    protected class B { } 
} 

答えて

24

内部クラスを使用する必要があります。 JVMは実際に内部クラスの概念を持っていないので、コンパイラはそれを操作しなければなりません。コンパイラは、クラスAのクラス「外部」を生成しますが、同じパッケージ内に作成し、合成アクセッサ/コンストラクタを追加して、Aがそのクラスにアクセスできるようにします。

Bに保護されたコンストラクタを渡すと、Aはコンストラクタを追加する合成コンストラクタを必要とせずに、同じパッケージ内にあるので、そのコンストラクタにアクセスできます。

+0

OK、わかりました。私の場合、これは例のような内部クラスの使用を避けることを意味し、混乱の原因となるだけです。 – Gerard

+2

私はそれがあなたを気にさせません。そのコンパイラの警告は、誰にもあまり使われていないので、合成メソッドは常に内部クラスで使用されており、大きな影響はありません。 – skaffman

+0

IMHO合成方法は、言語への不必要な追加でした。 "プライベート"メンバ(コンパイラがとにかくフードの下で行う)のパッケージスコープを使用するだけで満足のいく解決策でした。 – finnw

-1

あなたは基本的にJavaの1.1で導入されたハックです

this.new B(); 
+0

申し訳ありませんが、this.new B();同じ警告と動作を与えます。 – Gerard

+2

@Rats:それは手元の問題に何の違いも生じません。 「この」資格は暗黙のものです。 – skaffman

2

class Bとそのコンストラクタのアクセスは同じである必要はありません。パッケージスコープのコンストラクタを持つプライベート内部クラスを持つことができます。これは私が通常行うことです。

public class A { 
    public A() { b = new B(); } 
    B b; 
    private class B { 
    B() { } 
    } 
} 
+0

しかし、あなたはどのように適切にプライベートクラスのパッケージプライベートインスタンスを持つことができますか? – einpoklum

4

私はこの質問は今、ほぼ3歳ですが、私は問題の一部がまだ回答されていないことを見つける知っている:

そして:それはクラスBはもはやプライベートであることを意味していないん実行時に? skaffmans答えに

カルロスHeubergersコメントは、クラスBはまだパッケージ内の他のクラスのprivateで、示唆しています。

彼はおそらくJavaプログラミング言語に適しています。つまり、他のクラスのクラスBを参照することはできません。少なくともリフレクションを使用していない場合(プライベートクラスのメンバーも外部からアクセスできる)、これは別の問題です。

しかし、JVMは内部クラス(skaffman状態)の概念を持たないため、バイトコードレベルで「1つのクラスだけがアクセス可能」な可視性がどのように実現されているのか自分自身に尋ねました。答え:JVMの場合、内部クラスは通常のパッケージのプライベートクラスのように見えますが、それはまったく実現しません。これは、自分でバイトコードを書く(またはコンパイラによって生成されたコードを変更する)場合、問題なくクラスBにアクセスできます。

同じパッケージ内のすべてのクラスからすべての合成アクセサメソッドにアクセスできます。したがって、クラスBのメソッドのクラスAのプライベートフィールドに値を割り当てると、値を設定するクラスAaccess$000という名前)で、デフォルト(つまりパッケージプライベート)の可視性を持つ合成アクセサメソッドが生成されます。このメソッドは、クラスBから呼び出されることになっています(実際には、Java言語を使用して呼び出すことができます)。しかし、JVMの観点から見ると、これは他のクラスと同様のメソッドであり、どのクラスでも呼び出すことができます。

だから、質問に答えるために:ビューのJavaの言語の観点から

  • を、クラスBであり、民間のままです。
  • JVMの観点からは、クラスB(またはそれ以上:クラスA$B)はプライベートではありません。
+0

が正しいが、それは私が提案したものではない。私は "**メンバー**はまだプライベートです"と書いています - 私はクラスのフィールドではなく、あなた自身が解釈する**クラス**そのものではありませんでした!また、この質問は[タグ:Java]ではなく[タグ:バイトコード](世代、変更、_ハッキング_ ...)です。 –

+0

@CarlosHeubergerもちろんこれはあなたに違反ではありませんでした!クラスBのメンバーはまだプライベートですが、クラスB(クラスAのメンバーの一種として)は(JVMの観点からは)そうではありません。これはGerardの元の質問でした。そして、はい、質問はJavaについてですが、タグwikiに記載されているように: "Javaはプログラミング言語と**実行時環境**"です。また、ランタイム環境はJVMであり、JVM自体はJavaプログラミング言語とは関係ありませんが、バイトコードのみを解釈します。 – siegi

関連する問題