我有一个匿名的内部类和一个等效的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);
}
编辑:我确实遇到过一种解决方法:使用val
的getter。
答案 0 :(得分:11)
关于lambda expression bodies州的章节
与匿名类声明中出现的代码不同,的含义 名称以及出现在lambda正文中的
this
和super
个关键字, 以及引用声明的可访问性是相同的 如在周围的上下文(除了引入lambda参数 新名字。)
this
(显性和隐性)的透明度 lambda表达式 - 也就是说,将其视为与...相同 周围环境 - 允许更多的实施灵活性,以及 防止身体中不合格名字的含义 取决于超载分辨率。
因此,他们更加严格。
在这种情况下,周围的上下文是对字段的分配,而当前的问题是对字段val
,空白final
字段的访问,位于右侧表达。
Java语言规范说明
每个局部变量(第14.4节)和每个空白
final
字段(§4.12.4, §8.3.1.2)在访问时必须具有明确赋值 值发生。对其值的访问权限包含变量的简单名称 (或者,对于字段,由
this
限定的字段的简单名称) 发生在表达式中的任何地方,除了作为左边的操作数 简单赋值运算符=
(第15.26.1节)。对于本地变量或空白
final
字段x
的每次访问,x
必须为 在访问之前明确分配,或发生编译时错误。
然后继续说
让
C
成为一个类,让V
成为空白final
非static
成员字段 在C
中声明的C
。然后:在最左边的实例初始化程序(第8.6节)或实例变量之前,
V
肯定是未分配的(而且未明确分配) 初始化程序为C
。
V
在[{1}}的实例初始值设定项或实例变量初始值设定项之前[un]分配,而最左边的iffC
除外 在前面的实例初始化程序或实例之后分配[un] 变量初始值设定项V
。
您的代码基本上看起来像这样
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 = ...
的初始化表达式中访问时,确定val
未分配。
上述规则适用于使用简单名称lambdaGetValString
,而不适用于合格的表达式val
。你可以使用
this.val
答案 1 :(得分:3)
这不会编译:
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;
}
所以它与lambdas无关。 在执行构造函数之前,将对在其声明的同一行上初始化的字段进行求值。 所以在这一点上,变量&#39; val&#39; (或在此示例中&#39; x&#39;)尚未初始化。
答案 2 :(得分:0)
就我而言,我有一个Predicate
试图访问一个private final
实例变量。我也完成了Predicate
决赛的修复。
之前-编译器错误,可能尚未初始化this.availableCities
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);
}
}
我认为“可能尚未初始化”编译器错误有一些优点,因为否则,没有什么可以阻止您在初始化最终实例变量之前从构造函数中调用Predicate
:
编译器强制执行此操作的可能原因
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);
}