为什么 java lambda 表达式可以使用“this”关键字引用父类成员,而匿名类不能?[复制]

Why can java lambda expressions reference parent class members using "this" keyword whereas anonymous classes can not? [duplicate]

提问人:Grateful 提问时间:3/1/2023 更新时间:3/1/2023 访问量:117

问:

在以下课程中:

public class ThreadTest {

    public static String name = "Member Accessed";
    
    
    public void run() {
    
        // Anonymous class
        Runnable r1 = new Runnable() { 
        
            @Override
            public void run() {
                System.out.println("Using Anonymous class, referencing this.name " + this.name);
            }
        };
        
        // lambda expression
        Runnable r2 = () -> {
            System.out.println("Using Lambda Expression, referencing this.name " + this.name);
        };
        
        new Thread(r1).start();
        new Thread(r2).start();
    }


    public static void main(String[] args) {
    
        ThreadTest tt = new ThreadTest();
        tt.run();

    }
}

为什么 lambda 表达式可以访问 ,而匿名类不能?我知道对于匿名类,引用匿名类范围,并且不存在。但是,lambda 表达式基本上是一个匿名方法,它属于实现接口的匿名类。因此,从技术上讲,还应该引用 lambda 表达式的匿名类。但不知何故,设法引用了.this.namethisnamethisthisname

为什么会这样?

java lambda 这个

评论

1赞 Elliott Frisch 3/1/2023
但是,lambda 表达式基本上是一个匿名方法,它属于实现接口的匿名类不。事实并非如此。Lambda 表达式使用新的字节码机制和 LambdaMetafactory;Lambda 中没有匿名类。invokedynamic
0赞 Grateful 3/1/2023
@ElliottFrisch我知道这就是编译器级别发生的事情......但 lambda 是匿名方法。你不能否认这一点。但是,如果这个匿名方法不属于匿名类,那么你如何解释它实现接口的事实呢?我可以理解一个类能够实现一个接口......但是,方法到底是如何直接实现接口的呢?请解释一下。
0赞 Elliott Frisch 3/1/2023
请参阅 Java 8 中的函数接口有什么用途?
0赞 Grateful 3/1/2023
@ElliottFrisch 再次,非常感谢您的回复......但会更具体吗?你希望我关注哪一部分?我看了一下您提供的链接,但它无助于解释 lambda 表达式如何帮助实现没有匿名类的接口。请帮我详细说明一下。提前致谢!
1赞 Elliott Frisch 3/1/2023
JEPS-126 说(部分)目前首选的 lambda 表达式实现方法依赖于 JSR 292 引入的 invokedynamic 和方法句柄。您可能还会注意到,现在允许接口具有实现。这些天 Java 中有很多变化。default

答:

0赞 Nikolas Charalambidis 3/1/2023 #1

首先,只要引用实例而不是类变量,就可以解决这个问题:

public String name = "Member Accessed";

尽管每个 lambda 表达式都可以表示为匿名类,但它们的字节码不同,正如 Eliott Frisch 所说。我知识渊博,无法提供更多细节,但我可以向您推荐 Brian Goetz 的一篇非常有趣的读物。可悲的是,只要原始链接不再处于活动状态,就只能通过以下答案访问该脚本:Lambda 翻译策略


无论如何,匿名类可以通过使用 指定类名来指出外部类的实例变量。this

Runnable r1 = new Runnable() {

    @Override
    public void run() {
        System.out.println("Using Anonymous class: " + ThreadTest.this.name);
    }
};

评论

0赞 Grateful 3/1/2023
谢谢。我知道你可以从匿名的 Runnable 类中参考。但是,如果相应的 lambda 是匿名类(而不是匿名方法),那么为什么直接从 lambda 表达式工作,该表达式应该有自己的范围,因为它是一个匿名类。ThreadTest.this.namethis.name
0赞 Nikolas Charalambidis 3/1/2023
我说它们可以“表示”为匿名类,但并不是说它们是等价的,否则,它们的字节码也会相同,并且引用 through 将不起作用。我也花了一些时间来掌握这个:)this.name
0赞 Grateful 3/1/2023
谢谢你的诚实。在我看来,如果 lambda 不是匿名类,我们应该避免使用“lambda 可以表示为......”之类的词。我认为官方的预言机文档明确提到 lambda 是匿名函数......因此,让我们坚持下去。
0赞 Nikolas Charalambidis 3/1/2023
“表示为”并不意味着“等于”,但我明白你的意思。规范中没有任何地方表明 lambda 表达式是一个匿名类(这就是为什么我从不在我的答案中写 lambda 表达式“是”匿名类): docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.27 虽然,在一些 Oracle 网站上可能会出现 lambda 表达式“是”匿名类,但我还没有找到这样的页面。我绝对会选择不坚持他们是平等的。