从扩展外部类本身的内部类中访问外部类成员

在下面显示的代码片段中,内部类inheritance外部类本身。

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赋给TestInnerClass类的private字段value ,然后调用callShowValue()方法。

callShowValue()方法导致另一个字符串 – 在调用扩展TestInnerClassInnerClassshowValue()方法之前,将Another value设置为TestInnerClass类的私有字段value

因此, showValue()方法中有以下两个语句,

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

应该显示,

另一个价值
另一个价值

但他们显示,

初始值
初始值

为什么会这样?

方法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"被打印两次。 无法访问子类中的private成员。


关键是:不要使子类内部类。

这里的关键是理解内部类如何访问外部类的成员。 在privatenon-private成员的情况下,如何获得这些成员的资格。 ( 注意:我将在这里讨论非static内部类,因为问题仅限于此)。

内部类存储对封闭实例的引用:

内部类将对封闭实例的引用存储为字段。 该字段的名称为this$0 。 封闭实例始终绑定到内部类对象。 当您从封闭类内部创建内部类的对象时, this$0的参考值对于所有这些对象保持相同,但this引用将有所不同。

您可以在内部类中使用Outer.this语法访问this$0字段。 例如,考虑以下代码:

 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$0 。 您使用OuterClass.this引用明确地将成员访问限定为this$0 。 因此,考虑外部类中的字段valuepublic ,然后在内部类的showValue()方法中:

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

前两个打印语句是等效的。 它们将被编译为相同的字节代码:

  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限定字段或方法访问表达式,则会出现编译器错误:

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

上面的print语句不会编译,因为value不是内部类本身的字段。 这是一个外部阶级领域。 this指的是内部类实例,而不是外部实例。


当内部类扩展外部类时故事会发生变化:

当你的内部类扩展外部类时,那时事情开始变得怪异。 因为在这种情况下,使用this限定字段或方法访问权限对非私有成员有效。 对于private成员来说,这仍然无效,因为private成员不会被inheritance。

在inheritance内部类的情况下,直接访问外部类成员是合格的。 这意味着,他们将作为内部成员访问。 虽然使用Outer.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 } 

前两个print语句将打印内部类实例的value字段,而第三个print语句将打印封闭实例的value字段。 困惑?

记得我说过,当你从外部类中创建内部类的多个实例时,它们将具有相同的this$0引用。

考虑您创建一个外部类实例:

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

然后在callShowValue()方法中,创建一个内部类的实例:

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

现在, showValue()方法的输出将是:

 rj rj rohit 

您会注意到, this.valueOuter.this.value不同。

如果您将value字段private怎么办

现在,当您将外部类字段private ,当然您无法使用this.value;访问它this.value; 。 因此,第二个print语句将无法编译。

在这种情况下,直接访问该字段将通过this$0进行限定。 现在将字段value更改为private,并将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 } 

这就是问题所在。 根据字段是public字段还是private字段,第一个print语句使用this valuethis$0限定value


来你的具体问题:

现在在你的代码中,由于value field和getValue()方法都是privateshowValue()方法:

 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); } 

正在访问该字段和封闭实例的方法。 该领域仍然是初始值 。 这就是为什么输出是:

初始值
初始值

在上面的例子中, TestInnerClass和InnerClass之间有两种不同的关系。

但故事有一点点扭曲……有两个对象! 问题是我们将“另一个价值”放入不同的对象! 并打印旧的价值 ..

第一个对象:

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

Test Class中的上述方法创建一个具有“初始值”的TestInnerClass实例

第二个对象:

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

由于InnerClass正在扩展TestInnerClass ,因此使用“Another Value”创建另一个新的TestInnerClass实例。 但是我们从旧对象打印的值不是第二个。

其他答案已经解释了为什么你得到你看到的结果(你有两个TestInnerClass实例并正在访问第一个),但实际上有一种方法可以访问其子类中的TestInnerClass的私有成员 – 关键字super

如果用这个替换showValue方法:

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

您将获得预期的输出。

我还建议如果你决定这样做,你就让InnerClass成为一个静态内部类,因为它不再需要引用它的外部类的实例。