Java lambda 与匿名内部函数有不同的变量要求 类
Java lambdas have different variable requirements than anonymous inner classes
我有一个匿名内部 class 和一个等效的 lambda。为什么 lambda 的变量初始化规则更严格,有没有比匿名内部 class 或在构造函数中初始化它更干净的解决方案?
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。
这不会编译:
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;
}
所以这与 lambda 无关。
在执行构造函数之前评估在声明它的同一行上初始化的字段。
所以此时,变量'val'(或本例中的'x')尚未初始化。
lambda expression bodies 上的章节指出
Unlike code appearing in anonymous class declarations, the meaning of
names and the this
and super
keywords appearing in a lambda body,
along with the accessibility of referenced declarations, are the same
as in the surrounding context (except that lambda parameters introduce
new names).
The transparency of this
(both explicit and implicit) in the body of a
lambda expression - that is, treating it the same as in the
surrounding context - allows more flexibility for implementations, and
prevents the meaning of unqualified names in the body from being
dependent on overload resolution.
因此他们更加严格。
在这种情况下,周围的上下文是对字段的分配,手头的问题是对字段的访问,val
,一个空白的 final
字段,在右侧表达式的一侧。
Java 语言规范指出
Each local variable (§14.4) and every blank final
field (§4.12.4,
§8.3.1.2) must have a definitely assigned value when any access of its
value occurs.
An access to its value consists of the simple name of the variable
(or, for a field, the simple name of the field qualified by this
)
occurring anywhere in an expression except as the left-hand operand of
the simple assignment operator =
(§15.26.1).
For every access of a local variable or blank final
field x
, x
must be
definitely assigned before the access, or a compile-time error occurs.
接着说
Let C
be a class, and let V
be a blank final
non-static
member field
of C
, declared in C
. Then:
V
is definitely unassigned (and moreover is not definitely assigned) before the leftmost instance initializer (§8.6) or instance variable
initializer of C
.
V
is [un]assigned before an instance initializer or instance variable initializer of C
other than the leftmost iff V
is
[un]assigned after the preceding instance initializer or instance
variable initializer of 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
的初始化表达式中访问时未分配。
在我的例子中,我有一个 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);
}
}
我认为 "may not have been initialized" 编译器错误有一些优点,否则没有什么可以阻止您在初始化最终实例变量之前从构造函数中调用 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);
}
我有一个匿名内部 class 和一个等效的 lambda。为什么 lambda 的变量初始化规则更严格,有没有比匿名内部 class 或在构造函数中初始化它更干净的解决方案?
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。
这不会编译:
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;
}
所以这与 lambda 无关。 在执行构造函数之前评估在声明它的同一行上初始化的字段。 所以此时,变量'val'(或本例中的'x')尚未初始化。
lambda expression bodies 上的章节指出
Unlike code appearing in anonymous class declarations, the meaning of names and the
this
andsuper
keywords appearing in a lambda body, along with the accessibility of referenced declarations, are the same as in the surrounding context (except that lambda parameters introduce new names).The transparency of
this
(both explicit and implicit) in the body of a lambda expression - that is, treating it the same as in the surrounding context - allows more flexibility for implementations, and prevents the meaning of unqualified names in the body from being dependent on overload resolution.
因此他们更加严格。
在这种情况下,周围的上下文是对字段的分配,手头的问题是对字段的访问,val
,一个空白的 final
字段,在右侧表达式的一侧。
Java 语言规范指出
Each local variable (§14.4) and every blank
final
field (§4.12.4, §8.3.1.2) must have a definitely assigned value when any access of its value occurs.An access to its value consists of the simple name of the variable (or, for a field, the simple name of the field qualified by
this
) occurring anywhere in an expression except as the left-hand operand of the simple assignment operator=
(§15.26.1).For every access of a local variable or blank
final
fieldx
,x
must be definitely assigned before the access, or a compile-time error occurs.
接着说
Let
C
be a class, and letV
be a blankfinal
non-static
member field ofC
, declared inC
. Then:
V
is definitely unassigned (and moreover is not definitely assigned) before the leftmost instance initializer (§8.6) or instance variable initializer ofC
.
V
is [un]assigned before an instance initializer or instance variable initializer ofC
other than the leftmost iffV
is [un]assigned after the preceding instance initializer or instance variable initializer ofC
.
你的代码基本上是这样的
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
的初始化表达式中访问时未分配。
在我的例子中,我有一个 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);
}
}
我认为 "may not have been initialized" 编译器错误有一些优点,否则没有什么可以阻止您在初始化最终实例变量之前从构造函数中调用 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);
}