car*_*ret 3 java lambda java-8
我不明白这种行为.
这段代码符合:
public class A {
private String s;
private Function<String, String> f = e -> s;
public A(String s) {
this.s = s;
}
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我做s
最后的,那么我得到一个编译器错误:
public class A {
private final String s;
private Function<String, String> f = e -> s; // Variable 's' might not have been initialized
public A(String s) {
this.s = s;
}
}
Run Code Online (Sandbox Code Playgroud)
这是为什么?如果是另一种方式,我明白了,但是当我声明一个字段final
(这迫使我在构造函数中初始化它的值)时,编译器是怎么抱怨的,当它不是时它就可以了final
?
Ada*_*lik 12
它与lambda无关,这个例子有同样的错误:
public class Test {
private final String a;
private String b = a; // // Variable 'a' might not have been initialized
public Test(String a) {
this.a = a;
}
}
Run Code Online (Sandbox Code Playgroud)
这是因为声明位置的初始化是在构造函数之前执行的.因此,在声明的地方b
,a
仍然没有初始化.
使用此示例时很明显:
public class Test {
private String a = "init";
private String b = a;
public Test(String a) {
this.a = a;
}
public static void main(String[] args) {
System.out.println(new Test("constructor").b);
}
}
Run Code Online (Sandbox Code Playgroud)
当你运行它,它打印"init"
(到字段的值a
最初分配),而不是"constructor"
因为初始化b
发生之前运行的构造.
你的例子中的lambda使得它更复杂,因为我们可以预期,由于访问a
延迟,编译器就可以了,但显然编译器只遵循"不访问变量"的一般规则在它被初始化之前".
您可以使用访问器方法绕过它:
public class Test {
private final String a;
private String b = getA(); // allowed now, but not very useful
private Function<String, String> f = e -> getA(); // allowed now and evaluated at the time of execution of the function
public Test(String a) {
this.a = a;
}
public static void main(String[] args) {
System.out.println(new Test("constructor").b); // prints "null"
System.out.println(new Test("constructor").f.apply("")); // prints "constructor"
}
public String getA() {
return a;
}
}
Run Code Online (Sandbox Code Playgroud)
非最终成员变量将始终被初始化(因为它具有默认值 - null
在您的String
变量的情况下),因此不可能未初始化它.
另一方面,最终变量只能初始化一次,所以我假设它没有初始化为默认值.
我找到的最接近的相关内容是JLS 4.12.4.:
4.12.4.最终变量
变量可以声明为final.最终变量只能分配一次.如果分配了最终变量,那么这是一个编译时错误,除非它在分配之前肯定是未分配的
我假设我们可以理解这最后一句话意味着最终变量没有被赋予默认值,否则你将得到一个编译时错误this.s = s;
.
更好的JLS参考(感谢Holger)是JLS 16:
第16章.明确的分配
对于本地变量或空白最终字段x的每次访问,必须在访问之前明确分配x,否则发生编译时错误.
这个要求背后的理性是(在你的例子中)lambda表达式可以在s
初始化之前调用:
public A(String s) {
String v = f.apply("x"); // this.s is not initialized at this point
// so it can't be accessed
this.s = s;
}
Run Code Online (Sandbox Code Playgroud)
请注意,您可以在初始化最终变量后初始化构造函数中的lambda表达式(我将参数的名称更改为与成员变量不同,以便lambda表达式不会获取该局部变量):
public A(String so) {
// f = e -> s; // Error: The blank final field s may not have been initialized
this.s = so;
f = e -> s; // works fine
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
2092 次 |
最近记录: |