9

以下に示すコード スニペットでは、内部クラスが外部クラス自体を継承しています。

package test;

class TestInnerClass {

    private String value;

    public TestInnerClass(String value) {
        this.value = value;
    }

    private String getValue() {
        return value;
    }

    public void callShowValue() {
        new InnerClass("Another value").showValue();
    }

    private final class InnerClass extends TestInnerClass {

        public InnerClass(String value) {
            super(value);
        }

        public void showValue() {
            System.out.println(getValue());
            System.out.println(value);
        }
    }
}

public final class Test {

    public static void main(String[] args) {
        new TestInnerClass("Initial value").callShowValue();
    }
}

メソッド内の唯一のステートメントmain()(最後のスニペット) は、値Initial valueをクラスのプライベート フィールドvalueに割り当ててからTestInnerClass、メソッドを呼び出しますcallShowValue()

このメソッドは、拡張メソッドを呼び出す前に、callShowValue()別の文字列 -Another valueをクラスのプライベート フィールドに設定しvalueます。TestInnerClassshowValue()InnerClassTestInnerClass

showValue()したがって、メソッド内の次の 2 つのステートメントは、

System.out.println(getValue());
System.out.println(value);

表示する必要があり、

別の値
別の値

しかし、彼らは表示します、

初期値
初期値

なぜこれが起こるのですか?

4

4 に答える 4

8

メソッドgetValue()とフィールドvalueはどちらもprivate. そのため、サブクラスを含む他のクラスにはアクセスできません。それらは継承されません。

InnerClass#showValue()

public void showValue() {
    System.out.println(getValue());
    System.out.println(value);
}

それらはプライベートでgetValue()ありvalue、同じクラスにいるためにアクセスできる外部クラスのメンバーを参照しているという事実のためです。内部クラスは、外部クラスのプライベート メンバーにアクセスできます。上記の呼び出しは次と同等です

public void showValue() {
    System.out.println(TestInnerClass.this.getValue());
    System.out.println(TestInnerClass.this.value);
}

そして、あなたが設定valueしたので

new TestInnerClass("Initial value")

"Initial value"2 回印刷されていることがわかります。サブクラスでこれらのメンバーにアクセスする方法はありません。private


ポイントは、サブクラスを内部クラスにしないことです。

于 2013-10-13T19:41:36.780 に答える
8

ここで重要なのは、内部クラスが外部クラスのメンバーにアクセスする方法を理解することです。privateそして、メンバーの場合、これらのメンバーへのアクセスはどのように修飾されますかnon-private。(注:static質問はそれについてのみであるため、ここでは非内部クラスについて説明します)。

内部クラスは、囲んでいるインスタンスへの参照を格納します。

内部クラスは、外側のインスタンスへの参照をフィールドとして格納します。フィールドの名前はthis$0. 外側のインスタンスは、常に内部クラス オブジェクトにバインドされます。外側のクラスの内部から内部クラスのオブジェクトを作成すると、this$0それらの参照値はすべて同じままですが、this参照は異なります。

内部クラスの構文をthis$0使用してフィールドにアクセスします。Outer.thisたとえば、次のコードを検討してください。

class Outer {
    public Outer() { }

    public void createInnerInstance() {
        Inner obj1 = new Inner();
        Inner obj2 = new Inner();
    }

    private class Inner {
        public Inner() {
            System.out.println(Outer.this);
            System.out.println(this);
        }
    }
}

public static void main(String[] args) {
    new Outer().createInnerInstance();
}

このコードを実行すると、次のような出力が得られます。

Outer@135fbaa4
Outer$Inner@45ee12a7
Outer@135fbaa4
Outer$Inner@330bedb4

1番目と 3番目の参照は同じですが、2番目と 4番目の参照は異なることに注意してください。


this$0外部クラスのメンバーは、参照を使用して内部クラスでアクセスできます。

フィールドまたは内部クラスから外部クラスの他のメンバーにアクセスすると、アクセス式は自動的に修飾されますthis$0this$0参照を使用してメンバー アクセスを明示的に修飾しOuterClass.thisます。valueしたがって、外側のクラスのフィールドを考慮しpublic、次にshowValue()内側のクラスのメソッドで次のようにします。

public void showValue() {
    System.out.println(TestInnerClass.this.value);
    System.out.println(value);
}

最初の 2 つの print ステートメントは同等です。それらは同じバイト コードにコンパイルされます。

 public void showValue();
   Code:
      0: getstatic     #3                  // Field java/lang/System.out:Ljava/
o/PrintStream;
      3: aload_0
      4: getfield      #1                  // Field this$0:LTestInnerClass;
      7: getfield      #4                  // Field TestInnerClass.value:Ljava/lang/Stri
g;
     10: invokevirtual #5                  // Method java/io/PrintStream.printl
:(Ljava/lang/String;)V
     13: getstatic     #3                  // Field java/lang/System.out:Ljava/
o/PrintStream;
     16: aload_0
     17: getfield      #1                  // Field this$0:LTestInnerClass;
     20: getfield      #4                  // Field TestInnerClass.value:Ljava/lang/Stri
g;
     23: invokevirtual #5                  // Method java/io/PrintStream.printl
:(Ljava/lang/String;)V
     26: return

を使用して外部クラスのメンバーに明示的にアクセスすることはできませんthis

内部クラスでフィールドまたはメソッド アクセス式を明示的に修飾しようとするthisと、コンパイラ エラーが発生します。

public void showValue() {
    System.out.println(this.value);  // this won't compile
}

valueは内部クラス自体のフィールドではないため、上記の print ステートメントはコンパイルされません。それは外部クラスのフィールドです。this外側のインスタンスではなく、内側のクラス インスタンスを参照します。


内部クラスが外部クラスを拡張すると、ストーリーが変わります。

内部クラスが外部クラスを拡張するとき、物事がおかしくなり始めます。その場合、フィールドまたはメソッドへのアクセスを で修飾するとthis、非プライベート メンバーに対して有効になるためです。メンバーは継承されないため、メンバーprivateの場合はまだ有効ではありません。private

継承された内部クラスの場合、外部クラスのメンバーへの直接アクセスは で修飾されthisます。つまり、それらは内部クラス メンバーとしてアクセスされます。でアクセスを明示的に修飾している間Outer.this、囲んでいるインスタンスのフィールドを参照します - this$0.

valueフィールドが次のように宣言されていることを考慮してくださいpublic

public void showValue() { 
    System.out.println(value);            // inner class instance field
    System.out.println(this.value);       // inner class instance field
    System.out.println(Outer.this.value); // enclosing instance field
}

最初の 2 つの print ステートメントはvalue内部クラス インスタンスのフィールドを出力し、3 番目の print ステートメントは外側のインスタンスのvalueフィールドを出力します。混乱している?

外部クラスの内部から内部クラスの複数のインスタンスを作成すると、それらは同じthis$0参照を持つことになると言ったことを思い出してください。

外部クラスのインスタンスを作成するとします。

new Outer("rohit").callShowValue();

メソッドでcallShowValue()、内部クラスのインスタンスを作成します。

new Inner("rj").showValue();

ここで、showValue()メソッドの出力は次のようになります。

rj
rj
rohit

this.valueは とは異なることに気付くでしょうOuter.this.value

valueフィールドを作成するとどうなりますかprivate

さて、外部クラス field を作成するとprivate、もちろん を使用してアクセスすることはできませんthis.value;。したがって、2 番目の print ステートメントはコンパイルされません。

その場合のフィールドへの直接アクセスは、this$0この時間で修飾されます。ここで、フィールドvalueprivate を変更し、showValue()メソッドを次のように変更します。

public void showValue() { 
    System.out.println(value);            // enclosing instance field
    System.out.println(this.value);       // compiler error
    System.out.println(Outer.this.value); // enclosing instance field
}

ここに問題があります。最初の print ステートメントは、フィールドがまたはであるかどうかに基づいて、またはで修飾valueされます。thisthis$0publicprivate


あなたの具体的な問題に来て:

コードでは、valueフィールドとgetValue()メソッドの両方がprivateであるため、メソッドは次のようになりshowValue()ます。

public void showValue() {
    System.out.println(getValue());
    System.out.println(value);
}

と同じです:

public void showValue() {
    System.out.println(TestInnerClass.this.getValue());
    System.out.println(TestInnerClass.this.value);
}

これは、インスタンスを囲むフィールドとメソッドにアクセスしています。そして、フィールドはまだInitial Valueです。そのため、出力は次のようになります。

初期値
初期値

于 2013-10-13T20:48:45.677 に答える
2

上記の場合、 TestInnerClass と InnerClassの間に 2 つの異なる関係があります。

しかし、話には少しひねりがあります.. 2 つのオブジェクトがあります! 問題は、「別の値」を別のオブジェクトに入れていることです! 古いものからを出力します..

最初のオブジェクト:

public static void main(String[] args) {
    new TestInnerClass("Initial value").callShowValue();
}

上記の Test Class のメソッドは、 「初期値」で TestInnerClass のインスタンスを作成します

2 番目のオブジェクト:

public void callShowValue() {
    new InnerClass("Another value").showValue();
}

InnerClassは TestInnerClass を拡張しているため、TestInnerClass別の新しいインスタンスが"Another Value"で作成されます。ただし、2 番目のオブジェクトではなく、古いオブジェクトの値を出力しています。

于 2013-10-13T20:42:19.137 に答える
1

他の回答は、表示される結果が得られる理由をよく説明しています (の 2 つのインスタンスがTestInnerClassあり、最初のインスタンスにアクセスしています) 。TestInnerClasssuper

メソッドを次のように置き換えるshowValueと:

public void showValue() {
    System.out.println(super.getValue());
    System.out.println(super.value);
}

期待した出力が得られます。

InnerClassまた、これを行うことにした場合は、外部クラスのインスタンスへの参照が不要になるため、静的な内部クラスを作成することをお勧めします。

于 2014-07-25T21:32:39.047 に答える