Java lambda具有与匿名内部类不同的变量要求

Dav*_*ann 20 java lambda anonymous-inner-class

我有一个匿名的内部类和一个等效的lambda.为什么变量初始化规则对lambda更严格,是否有比匿名内部类更清晰的解决方案或在构造函数中初始化它?

import java.util.concurrent.Callable;

public class Immutable {
    private final int val;

    public Immutable(int val) { this.val = val; }

    // Works fine
    private final Callable<String> anonInnerGetValString = new Callable<String>() {    
        @Override
        public String call() throws Exception {
            return String.valueOf(val);
        }
    };

    // Doesn't compile; "Variable 'val' might not have been initialized"
    private final Callable<String> lambdaGetValString = () -> String.valueOf(val);
}
Run Code Online (Sandbox Code Playgroud)

编辑:我确实遇到了一个解决方法:使用getter for val.

Sot*_*lis 11

有关lambda表达式主体的章节说明

与出现在匿名类的声明,代码名称的含义以及thissuper在拉姆达身体出现关键词,与引用声明的可访问性一起,是同在周围的上下文(除了拉姆达参数引入新的名称).

thislambda表达式的主体(包括显式和隐式)的透明度- 也就是说,将其视为与周围上下文相同 - 允许实现更灵活,并防止正文中非限定名称的含义依赖于超载分辨率.

因此,他们更加严格.

在这种情况下,周围的上下文是对字段的赋值,而当前的问题是对表达式右侧的字段(val空白final字段)的访问.

Java语言规范说明

final当对其值进行任何访问时,每个局部变量(第14.4节)和每个空白字段(第4.12.4节,第8.3.1.2节)必须具有明确赋值.

对其值的访问包括变量的简单名称(或者,对于字段,限定字段的简单名称this),除了作为简单赋值运算符的左手操作数=(第15.26节)外,它出现在表达式的任何位置. 1).

对于本地变量或空白final字段的每次访问x,x必须在访问之前明确分配,否则发生编译时错误.

然后继续说

让我们C成为一个类,并V成为一个空白的finalstatic成员字段C,声明在C.然后:

  • V肯定是在最左边的实例初始化器(第8.6节)或实例变量初始化器之前未分配(并且还没有明确分配)C.

  • V在前一个实例初始化器或实例变量初始化器之后,C在最左边的iff 之前[un]赋值的实例初始值设定项或实例变量初始V值设定项之前[un]赋值C.

你的代码基本上是这样的

private final int val;
// leftmost instance variable initializer, val still unassigned 
private final Callable<String> anonInnerGetValString = ...
// still unassigned after preceding variable initializer
private final Callable<String> lambdaGetValString = ...
Run Code Online (Sandbox Code Playgroud)

因此,编译器val在初始化表达式中访问时确定未分配lambdaGetValString.

上述规则适用于使用简单名称val,而不适用于限定表达式this.val.您可以使用

final Callable<String> lambdaGetValString = () -> String.valueOf(this.val);
Run Code Online (Sandbox Code Playgroud)