下列类包含一个成员变量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
?
错误#JDK-8027941对此进行了精确描述。项目Lambda规范负责人Dan Smith写道,这不是错误,并且不仅限于lambda。
在有关错误报告的评论中,他这样说:
8.3.2.3:首先,通常禁止在字段初始化程序中使用字段,如果该使用发生在字段声明之前。规范对此并不十分清楚,但是一直的意图是“之前”包括该字段自己的初始化程序。因此,“
int x = x+1;
”不是有效的字段声明。
他还说:
可能会添加一个功能,该功能将像对待匿名类的主体一样专门处理lambda主体(或者,更广泛地说,如果它是变量初始值设定项,则允许lambda引用自身),但这尚未完成。(FWIW,直接修改8.3.2.3并不完全安全,就像第四个项目符号当前并不完全安全:“
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);
/* ... */
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句