![](/img/trans.png)
[英]Performance difference between Java 8 lambdas and anonymous inner classes
[英]Java lambdas have different variable requirements than anonymous inner classes
我有一个匿名内部类和一个等效的 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。
关于lambda 表达式主体的章节指出
与匿名类声明中出现的代码不同,名称的含义以及出现在 lambda 主体中的
this
和super
关键字以及引用声明的可访问性与周围上下文中的相同(除了 lambda 参数引入了新名称)。lambda 表达式主体中的
this
(显式和隐式)的透明度 - 即,将其与周围上下文中的处理方式相同 - 为实现提供了更大的灵活性,并防止了主体中未限定名称的含义相互依赖关于重载决议。
因此,他们更加严格。
在这种情况下,周围的上下文是对字段的赋值,而手头的问题是对表达式右侧的字段val
的访问,这是一个空白的final
字段。
Java 语言规范指出
每个局部变量(第 14.4 节)和每个空白的
final
字段(第 4.12.4 节,第 8.3.1.2 节)在对其值进行任何访问时都必须具有明确分配的值。对其值的访问包括变量的简单名称(或者,对于字段,由
this
限定的字段的简单名称)出现在表达式中的任何地方,除了简单赋值运算符=
(§ 15.26.1)。对于局部变量或空白
final
字段x
每次访问,必须在访问之前明确分配x
,否则会发生编译时错误。
然后它继续说
设
C
为一个类,并设V
为C
的空白final
非static
成员字段,在C
声明。 然后:
V
在最左边的实例初始化器(第 8.6 节)或C
实例变量初始化器之前肯定是未分配的(而且也不是明确分配的)。
V
是[未]初始化一个实例或实例变量初始值的前分配C
比最左边当且仅当其它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 = ...
因此,当在lambdaGetValString
的初始化表达式中访问该val
时,编译器会确定该val
处于未分配状态。
上述规则适用于简单名称val
,而不适用于限定表达式this.val
。 你可以使用
final Callable<String> lambdaGetValString = () -> String.valueOf(this.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;
}
所以这与 lambdas 无关。 在执行构造函数之前评估在声明它的同一行上初始化的字段。 因此,此时变量“val”(或在本例中为“x”)尚未初始化。
就我而言,我有一个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);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.