在 Java 1.1 版本中将内部类添加到 Java 时,它们最初被定义为对 1.0 兼容代码的转换。如果您看一下这种转换的示例,我认为它会使内部类的实际工作方式更加清晰。
考虑 Ian Roberts 的回答中的代码:
public class Foo {
int val;
public Foo(int v) { val = v; }
class Bar {
public void printVal() {
System.out.println(val);
}
}
public Bar createBar() {
return new Bar();
}
}
当转换为 1.0 兼容代码时,该内部类Bar
将变为如下所示:
class Foo$Bar {
private Foo this$0;
Foo$Bar(Foo outerThis) {
this.this$0 = outerThis;
}
public void printVal() {
System.out.println(this$0.val);
}
}
内部类名称以外部类名称为前缀,以使其唯一。添加了一个隐藏的私有this$0
成员,其中包含外部的副本this
。并创建一个隐藏的构造函数来初始化该成员。
如果你看一下这个createBar
方法,它会变成这样的:
public Foo$Bar createBar() {
return new Foo$Bar(this);
}
那么让我们看看执行以下代码时会发生什么。
Foo f = new Foo(5);
Foo.Bar b = f.createBar();
b.printVal();
首先,我们实例化一个实例Foo
并将val
成员初始化为 5(即f.val = 5
)。
接下来我们调用f.createBar()
,它实例化 的实例Foo$Bar
并将成员初始化为从(ie )传入this$0
的值。this
createBar
b.this$0 = f
最后我们调用b.printVal()
which 尝试打印b.this$0.val
which is f.val
which is 5。
现在这是内部类的常规实例化。让我们看看Bar
从外部实例化时会发生什么Foo
。
Foo f = new Foo(5);
Foo.Bar b = f.new Bar();
b.printVal();
再次应用我们的 1.0 转换,第二行将变成这样:
Foo$Bar b = new Foo$Bar(f);
这几乎与f.createBar()
调用相同。我们再次实例化一个实例Foo$Bar
并将this$0
成员初始化为 f。再说一次,b.this$0 = f
。
再一次,当你打电话时b.printVal()
,你正在打印b.thi$0.val
哪个f.val
是 5。
要记住的关键是内部类有一个隐藏成员this
,其中包含外部类的副本。当您从外部类中实例化一个内部类时,它会隐式地使用当前值初始化this
。当您从外部类外部实例化内部类时,您可以通过new
关键字的前缀明确指定要使用的外部类的哪个实例。