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

Java lambda与匿名内部类具有不同的变量需求,java,lambda,anonymous-inner-class,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

我有一个匿名的内部类和一个等价的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);
}
import java.util.concurrent.Callable;
公共类不可变{
私人终审法院;
公共不可变(int-val){this.val=val;}
//很好
private final Callable anonInnerGetValString=new Callable(){
@凌驾
公共字符串调用()引发异常{
返回字符串.valueOf(val);
}
};
//未编译;“变量“val”可能尚未初始化”
私有最终可调用lambdaGetValString=()->String.valueOf(val);
}
编辑:我确实遇到了一个解决方法:使用getter for
val

这不会编译:

public class Example
{
  private final int x;
  private final int y = 2 * x;

  public Example() {
    x = 10;
  }
}
但这将:

public class Example
{
  private final int x;
  private final int y;

  public Example() {
    x = 10;
    y = 2 * x;
  }
}
这也将:

public class Example
{
  private final int x = 10;
  private final int y = 2 * x;
}
所以这与兰博达斯无关。 在同一行上初始化的字段在构造函数执行之前进行求值。 因此,此时变量“val”(或本例中的“x”)尚未初始化。

有关状态的章节

与出现在匿名类声明中的代码不同,的含义 名称和出现在lambda正文中的
this
super
关键字, 除了引用声明的可访问性之外,它们都是相同的 与周围环境一样
(除了lambda参数引入 新名称)

(显式和隐式)在 lambda表达式-即,将其与 周围环境—允许实现更大的灵活性,以及 防止正文中非限定名称的含义被忽略 取决于过载分辨率

正因为如此,他们才更加严格

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

Java语言规范说明

每个局部变量(§14.4)和每个空白
final
字段(§4.12.4), §8.3.1.2)在访问其 值出现

对其值的访问包括变量的简单名称 (或者,对于字段,由
this
限定的字段的简单名称) 在表达式中的任何位置出现,但作为表达式的左操作数除外 简单赋值运算符
=
(§15.26.1)

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

然后它接着说

C
设为类,将
V
设为空
final
静态
成员字段 属于
C
,在
C
中声明。然后:

  • V
    在最左边的实例初始值设定项(§8.6)或实例变量之前肯定未赋值(而且也没有肯定赋值)
    C
    的初始值设定项

  • V
    C
    的实例初始值设定项或实例变量初始值设定项之前[取消]赋值,而不是在最左边的iff
    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 = ...

在我的例子中,我有一个
谓词
,它试图访问一个
私有final
实例变量。我还将
谓词
设置为final,从而修复了它

之前-编译器错误,此.availableCities可能尚未初始化

class Service {
  private final List<String> availableCities;
  Service(List<String> availableCities) {
    this.availableCities = availableCities;
  }
  private Predicate<String> isCityAvailable = city -> this.availableCities.contains(city);
}

相关:有趣的是,如果您删除
val
上的
final
修饰符,当您将
String.valueOf(val)
更改为
String.valueOf(Immutable.this.val)
时,它会编译too@csharpfolk
这个
而不是
不可变的。这个
也可以用。@lucasvw我正在使用这个“在线java ide”site:,在那里,它不能仅与
一起使用。请定义同一行。
class Service {
  private final List<String> availableCities;
  Service(List<String> availableCities) {
    this.availableCities = availableCities;
  }
  private Predicate<String> isCityAvailable = city -> this.availableCities.contains(city);
}
class Service {
  private final List<String> availableCities;
  private final Predicate<String> isCityAvailable;
  Service(List<String> availableCities) {
    this.availableCities = availableCities;
    this.isCityAvailable = city -> this.availableCities.contains(city);
  }
}
class Service {
  private final List<String> availableCities;
  Service(List<String> availableCities, String topCity) {
    boolean isTopCityAvailable = isCityAvailable.test(topCity); // Error: this.availableCities is not initialized yet
    this.availableCities = availableCities;
  }
  private Predicate<String> isCityAvailable = city -> this.availableCities.contains(city);
}