从扩展外部类本身的内部类中访问外部类成员
在下面显示的代码片段中,内部类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()
方法导致另一个字符串 – 在调用扩展TestInnerClass
的InnerClass
的showValue()
方法之前,将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
成员。
关键是:不要使子类内部类。
这里的关键是理解内部类如何访问外部类的成员。 在private
和non-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
。 因此,考虑外部类中的字段value
是public
,然后在内部类的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.value
与Outer.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
value
或this$0
限定value
。
来你的具体问题:
现在在你的代码中,由于value
field和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); }
正在访问该字段和封闭实例的方法。 该领域仍然是初始值 。 这就是为什么输出是:
初始值
初始值
在上面的例子中, 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
成为一个静态内部类,因为它不再需要引用它的外部类的实例。