如果在使用IntelliJ IDEA调试Java时,一个变量的名称为"this$0",那么这意味着什么?

10

enter image description here

我正在尝试通过以调试模式运行名为testSendStream测试并逐步执行代码来理解this函数响应式Java库。
上面的快照显示,有一个名为this$0的奇怪命名的变量。
这个名字从哪里来?
这个名字代表什么意思?
为什么这个变量有这个名字?
给它这个名字的理由是什么?
当然,这个名字不是来自代码本身,而是由IntelliJ或javac/java生成的。但是为什么?
如果我用标签Mystery Object标记这个对象,那会发生什么也很有趣。

enter image description here

2个回答

17

this$0Inner类(非静态嵌套类)中的“隐藏字段”,用于保存对创建当前Inner类实例的Outer类实例的引用。

简而言之,当您拥有:

Outer outer = new Outer();
Outer.Inner inner = outer.new Outer.Inner(); 
Inner的实例由inner持有,在其this$0字段中存储对创建它的Outer实例的引用(与outer变量持有的引用相同)。
这是必要的,因为嵌套类必须访问外部类的所有成员(包括私有成员)。如果我们想在内部类中编写像methodFromOuterClass();这样的代码,JVM需要知道应该在哪个Outer实例上调用此方法。为了使它成为可能,编译器将这样的代码“更改”为this$0.methodFromOuterClass()
稍微详细解释并提供一个示例:
public class Outer {
    private int id;
    public Outer(int id) { this.id = id;}

    public class Inner{
        void printOuterID(){
            System.out.println(id); 
        }
    }
}

现在这里将会打印什么内容,为什么会是这样?
Outer o1 = new Outer(1);
Outer o2 = new Outer(2);
Outer.Inner in1 = o1.new Inner();
Outer.Inner in2 = o2.new Inner();

in1.printOuterID();
in2.printOuterID();

我们将会看到。
1
2

但是,in1 如何知道它应该从 o1 而不是 o2 中打印 id 的值呢?
这是因为每个内部类的实例都知道它是在哪个外部类实例上创建的。这是由 this$0 引用引起的,该引用存储对用于创建内部实例的外部实例的引用。
编译器通过将此变量添加到所有非静态内部类中,当您调用时设置其值。
Outer.Inner in1 = o1.new Inner(); //`this$0` will be set to hold `o1` instance.

所以像这样的代码

void printOuterID(){
    System.out.println(id); 
  //OR
  //System.out.println(Outer.this.id);
}

编译成
void printOuterID(){
    System.out.println(this$0.id); //although we can't access this$0 explicitly
}

顺便提一下,如果您的内部类不需要访问其任何外部类的非静态成员,则可以将其更改为static class,这将摆脱this$0字段。


也许值得一提的是,内部类不需要使用 Outer.this,而且可以将内部类定义为 静态 嵌套类。有时候会忘记使用 static 关键字。 - Joop Eggen
@JoopEggen 完成 - 假设我正确理解了您的意思。如果我没有理解正确,可以随意改进它。 - Pshemo

2

这与非静态内部类相关的惯例有关。 内部类的字节码将包含对名为this$0的包作用域字段的引用,该字段允许您引用封闭类的this对象。 请注意,在您的示例中,this$0与上面定义的Mystery Object this变量相同。


感谢您的有益回答! - jhegedus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接