为什么从线程调用 class 函数在 Java 中有效?

Why calling class function from a thread works in Java?

所以我得到了这个奇怪的场景,它工作正常,但对于它为什么工作没有任何意义。根据我在 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@6720b744
working fine
testFunc: com.company.Practice@5e2de80c

为什么!?为什么这个工作?我如何才能从 Runnable 中调用 testFunction()?我不应该创建一个新实例,然后像 Practice p = new Practice(); p.testFunction() 那样调用该函数吗? Java 如何知道 testFunction()Practice class 而不是 Runnable 的一部分?

还有,testFunction()this的值怎么和start()一样?不应该和run()一样吗?

你在这里有一个“内心class”。

内部 class 的实例(您的 Runnable 的匿名子 class )将包含对它们创建的包含外部 classes 的引用(这里是 Practice 的实例)并且可以在那些外部实例上调用方法。

你的 testFunction(); 被编译成 this.$myOuterInstance.testFunction();

How does Java know that testFunction() is part of Practice class and not Runnable?

编译器通过首先查看内部 class 然后转到外部作用域来解决这个问题,它不仅包括包含 class,还包括任何有效的最终局部变量有那个 start 方法。

这也类似于变量和字段的解析方式(当您有名称冲突时使用一些阴影规则)。

And also, how come the value of this in testFunction() is same as start()? Shouldn't it be same as run()?

没有。 testFunctionPractice 的实例上调用(与 start 一样)。

run 在您的 Runnable Practice.

实例上被调用

您可以访问testFunction是因为您使用的是testFunction简称。如果你这样做了:

this.testFunction();

那么您将无法访问它。

当您使用简单名称调用方法时,范围内的所有内容都被视为解析该方法。 (参见JLS 6.5.7.1testFunction肯定在Runnable的范围内,因为它在Practice.

要调用testFunction,你还需要一个Practice的实例,对吧?好吧,编译器生成一些代码,将调用 startPractice 实例(即 startthis 的含义)传递给 [=16= 的实例] 你正在创造。具体来说,它创建了一个非静态内部 class

class Practice {
    public void start() {
        ...
        new Thread(new SomeGeneratedRunnable()).start();
    }
    // not the actual name of the class. The actual name is Practice
    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里面的thisSomeGeneratedRunnable(或者Practice)的一个实例,但是testFunction里面的this是[=17=的一个实例].