2014-07-01 28 views
17

以下类包含一个用匿名内部类的实例初始化的成员变量runnable。内部类引用相同的部件:为什么Java 8中的lambdas不允许向匿名类不提供成员变量的前向引用?

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

这只要构件已经被分配和JLS允许这样的引用之前不执行该方法是没有问题的。用下面的

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

据我所知,这在功能上等同于前面的例子,但它也被拒绝通过javac 1.8.0_05

成员变量的声明理论上可以转化成lambda表达式这样错误信息:

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

虽然该陈述是真实的,但我不明白为什么这是不允许的。这是故意不允许的,也许是因为lambda表达式被编译为不同的字节码,如果允许,会导致问题?或者仅仅因为在匿名内部类中使用这些引用时存在问题而被禁止使用?还是无意中被JLS作者拒绝?或者它是javac中的一个错误?

+8

在eclipse中,这个工程:'Runnable runnable =() - > System.out.println(this。runnable);'只有在添加'this'限定符后才会失败。 –

回答

23

Bug #JDK-8027941正好描述了这一点。 Dan Smith(项目Lambda规范负责人)写道,这不是一个bug,也不限于lambda。

在上a related bug report的意见,他提出这样的:

8.3.2.3:首先,如果字段声明之前出现的使用“使用”的字段初始场的一般禁止。规范在这方面并不十分清楚,但意图一直是“之前”包含该领域自己的初始化器。所以“int x = x+1;”不是有效的字段声明。

他还备注:

将可能添加一个功能,将特殊对待拉姆达机构,像匿名类的机构(或更一般地,允许lambda来指代自己如果它是一个变量初始值设定项),但这还没有完成。 (FWIW的8.3.2.3一个简单的调整不会是完全安全的,就像子弹4目前不完全安全的。“Function f = (Function) ((Function) 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); 

    /* ... */ 
} 
相关问题