所以我得到了这个奇怪的场景,工作得很好,但没有任何意义,因为它为什么工作。根据我在c++中的经验,我非常确定这根本不起作用,并且会在编译期间抛出错误。
public class Practice {
private void testFunction() {
System.out.println("working fine");
System.out.println("testFunc: " + this);
}
public void start() {
System.out.println("start: " + this);
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("run: " + this);
testFunction();
}
}).start();
}
}
// Inside main
Practice practice = new Practice();
practice.start()
输出start: com.company.Practice@5e2de80c
run: com.company.Practice$1@6720b744
working fine
testFunc: com.company.Practice@5e2de80c
为什么! ?为什么会这样呢?我如何能够从一个Runnable调用testFunction()
?我是不是应该创建一个新的实例,然后像Practice p = new Practice(); p.testFunction()
那样调用这个函数?Java怎么知道testFunction()
是Practice
类的一部分而不是Runnable
?
还有,为什么testFunction()
中this
的值与start()
相同?它不应该和run()
一样吗?
有一个"内部类";这里。
内部类的实例(Runnable的匿名子类)将包含对创建它们的外部类的引用(这里是Practice的实例),并且可以调用这些外部实例的方法。
你的testFunction();
被编译成this.$myOuterInstance.testFunction();
Java如何知道testFunction()是Practice类的一部分而不是Runnable?
编译器通过首先查看内部类,然后转到外部作用域来解决这个问题,外部作用域不仅包括包含类,还包括在start
方法中可能具有的任何有效的最终局部变量。
这与变量和字段的解析方式类似(当您有名称冲突时使用一些阴影规则)。
而且,
testFunction()
中的值如何与start()
相同?它不应该和run()
一样吗?
。testFunction
在Practice
的实例上被调用(start
也是)。
run
在你的可运行Practice$1
的实例上被调用。
您可以访问testFunction
,因为您正在使用testFunction
的简单名称。
this.testFunction();
那么你就不能访问它了
当你用简单的名字调用一个方法时,范围内的所有东西都被认为是解析这个方法的。(参见JLS 6.5.7.1)testFunction
肯定在Runnable
的范围内,因为它在Practice
的范围内。
要调用testFunction
,您仍然需要Practice
的实例,对吗?好吧,编译器生成一些代码,将Practice
的实例传递给start
调用(即this
在start
中的含义)到您正在创建的Runnable
实例。具体来说,它创建了一个非静态内部类
class Practice {
public void start() {
...
new Thread(new SomeGeneratedRunnable()).start();
}
// not the actual name of the class. The actual name is Practice$1
private class SomeGeneratedRunnable implements Runnable {
public void run() {
System.out.println("run: " + this);
testFunction();
}
}
}
你可以这样想:
class Practice {
public void start() {
...
new Thread(new SomeGeneratedRunnable(this)).start();
}
}
class SomeGeneratedRunnable implements Runnable {
private final Practice outer;
public SomeGeneratedRunnable(Practice outer) {
this.outer = outer;
}
public void run() {
System.out.println("run: " + this);
outer.testFunction();
}
}
现在你应该明白为什么testFunction
中的this
不可能与Runnable
中的this
相同。Runnable
中的this
是SomeGeneratedRunnable
(或Practice$1
)的实例,而testFunction
中的this
是Practice
的实例。