为什么 Java 8 中的 lambda 不允许前向引用匿名类不允许的成员变量? [英] Why do lambdas in Java 8 disallow forward reference to member variables where anonymous classes don't?

查看:28
本文介绍了为什么 Java 8 中的 lambda 不允许前向引用匿名类不允许的成员变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面的类包含一个成员变量 runnable,它是用匿名内部类的实例初始化的.内部类引用同一个成员:

The following class contains a member variable runnable which is initialized with an instance of an anonymous inner class. The inner class references the same member:

class Example {
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            System.out.println(runnable);
        }
    };
}

只要在分配成员之前不执行该方法并且 JLS 允许这样的引用,这不是问题.

This is not a problem as long as the method is not executed before the member has been assigned and the JLS allows such a reference.

成员变量的声明理论上可以转换成这样的 lambda 表达式:

The declaration of the member variable could theoretically be converted to a lambda expression like this:

Runnable runnable = () -> System.out.println(runnable);

据我所知,这在功能上等同于前面的示例,但被 javac 1.8.0_05 拒绝并显示以下错误消息:

To my understanding, this is functionally equivalent to the previous example, but it is rejected by javac 1.8.0_05 with the following error message:

Error:(2, 54) java: self-reference in initializer

虽然这句话是对的,但我不明白为什么不允许这样做.这是故意禁止的,也许是因为 lambda 表达式被编译为不同的字节码,如果允许会导致问题?或者只是因为这些引用在匿名内部类中使用时已经存在问题而被禁止?还是 JLS 的作者无意中禁止了它?或者是javac的bug?

While that statement is true, I do not see why this was disallowed. Was this intentionally disallowed, maybe because lambda expressions are compiled to different byte code which would lead to problems if it was allows? Or was just disallowed because there were already problems with these references when they were used in anonymous inner classes? Or was it unintentionally disallowed by the JLS writers? Or is it a bug in javac?

推荐答案

Bug #JDK-8027941准确地描述了这一点.Dan Smith(Lambda 项目规范负责人)写道,这不是错误,而且不仅限于 lambda.

Bug #JDK-8027941 describes exactly this. Dan Smith (Project Lambda Specification Lead) writes that it's not a bug, and not limited to lambdas.

在对相关错误报告的评论中,他是这样写的:

In the comments on a related bug report, he puts it like this:

8.3.2.3:首先,如果使用发生在字段声明之前,通常禁止在字段初始值设定项中使用"字段.规范对此不是很清楚,但意图一直是之前"包括字段自己的初始化程序.所以 "int x = x+1;" 不是有效的字段声明.

8.3.2.3: First, a "use" of a field in a field initializer is generally prohibited if the use occurs before the field declaration. The spec is not very clear on this, but the intent has always been that "before" includes the field's own initializer. So "int x = x+1;" is not a valid field declaration.

他还说:

可以添加一个特殊处理 lambda 主体的功能,例如匿名类的主体(或者,更一般地说,如果 lambda 是变量初始化程序,则允许它引用自身),但这还没有完成.(FWIW,对 8.3.2.3 的直接调整并不完全安全,就像第 4 项目前并不完全安全:Function f = (Function) ((Function) e -> f.apply(e)).apply(null);".)

It would possible to add a feature that would treat lambda bodies specially, like bodies of anonymous classes (or, more generally, allow a lambda to refer to itself if it is a variable initializer), but this has not been done. (FWIW, a straightforward tweak of 8.3.2.3 would not be entirely safe, just like the 4th bullet is not currently entirely safe: "Function f = (Function) ((Function) e -> f.apply(e)).apply(null);".)

我认为问题在于 Java 的设计者希望有简单的句法规则来决定允许使用哪种语句,而不是依赖于更复杂的语义代码分析.好处可能是规范更简单,因此对编译器的要求更少,而代价是程序员无法表达每个程序——至少不是他们想要的方式.

I think the problem is that the designers of Java want to have simple, syntactic rules to decide what kind of statements are allowed, rather than depending on more complex, semantic code analysis. The benefit is probably a simpler spec and therefore less requirements on compilers, while the cost is that programmers can't express every program -- at least not in the way they want to.

正如 Marko Topolnik 所指出的,有一个解决方案:完全限定该领域.错误报告中的示例:

As Marko Topolnik points out, there is a solution: fully qualify the field. Example from the bug report:

import java.util.function.Function; 

public class LambdaSelfRef { 

    // COMPILATION FAILURE 
    public static Function<Object, Object> op1 = e -> op1.apply(e); 

    // COMPILES OK 
    public static Function<Object, Object> op2 = e -> LambdaSelfRef.op2.apply(e); 

    /* ... */
}

这篇关于为什么 Java 8 中的 lambda 不允许前向引用匿名类不允许的成员变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆