为什么Java8中的lambdas不允许向前引用匿名类不使用的成员变量';T

为什么Java8中的lambdas不允许向前引用匿名类不使用的成员变量';T,lambda,java-8,javac,Lambda,Java 8,Javac,下面的类包含一个成员变量runnable,该变量由匿名内部类的实例初始化。内部类引用同一成员: class Example { Runnable runnable = new Runnable() { @Override public void run() { System.out.println(runnable); } }; } 只要在分配成员之前不执行方法,并且JLS允许这样的引用,这就不是问题 理论

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

class Example {
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            System.out.println(runnable);
        }
    };
}
只要在分配成员之前不执行方法,并且JLS允许这样的引用,这就不是问题

理论上,成员变量的声明可以转换为lambda表达式,如下所示:

Runnable runnable = () -> System.out.println(runnable);
据我所知,这在功能上与前面的示例相同,但被
javac 1.8.0_05
拒绝,并显示以下错误消息:

Error:(2, 54) java: self-reference in initializer
虽然这一说法是正确的,但我不明白为什么不允许这样做。这是否是故意不允许的,可能是因为lambda表达式被编译为不同的字节码,如果允许的话,这会导致问题?或者仅仅因为在匿名内部类中使用这些引用时已经存在问题而被禁止?还是JLS的作者无意中不允许?或者它是
javac
中的一个bug?

正是这样描述的。Dan Smith(Lambda项目规范负责人)写道,这不是一个bug,也不限于Lambda

在评论中,他这样说:

Runnable runnable = () -> System.out.println(runnable);
8.3.2.3:首先,如果在字段声明之前使用字段,则通常禁止在字段初始值设定项中“使用”字段。规范对此不是很清楚,但其目的始终是“之前”包括字段自己的初始值设定项。因此“
intx=x+1;
”不是有效的字段声明

他还说:

可以添加一个特别处理lambda实体的特性,比如匿名类的实体(或者更一般地说,如果lambda是变量初始值设定项,则允许lambda引用自身),但这还没有完成。(FWIW,8.3.2.3的一个简单的调整不会完全安全,就像第四个项目符号目前不是完全安全的:“
函数f=(函数)((函数)e->f.apply(e)).apply(null);

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


正如Marko Topolnik指出的,有一个解决方案:完全限定字段。错误报告中的示例:

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); 

    /* ... */
}
import java.util.function.function;
公共类LambdaSelfRef{
//编译失败
公共静态函数op1=e->op1.apply(e);
//编译OK
公共静态函数op2=e->LambdaSelfRef.op2.apply(e);
/* ... */
}

在eclipse中,这是有效的:
Runnable Runnable=()->System.out.println(this.Runnable)只有在没有添加
限定符的情况下才会失败。