【发布时间】:2017-12-22 13:31:52
【问题描述】:
以下代码不能用 javac 1.8.0_144 和 ecj 编译:
private LongSupplier foo() {
long fileSize;
try {
fileSize = canThrow();
} catch (IOException e) {
fileSize = 42;
}
LongSupplier foo = () -> 1 + fileSize;
return foo;
}
我想知道这是否是编译器中的错误。 The definition of effectively final in the JLS 是:
某些未声明为 final 的变量反而被认为是有效的 final:
如果满足以下所有条件,则声明器具有初始化程序(第 14.4.2 节)的局部变量实际上是最终变量:
它没有被宣布为最终的。
它永远不会出现在赋值表达式的左侧(第 15.26 节)。 (注意局部变量声明器包含
初始化器不是赋值表达式。)它永远不会作为前缀或后缀递增或递减运算符的操作数出现(第 15.14 节、第 15.15 节)。
如果满足以下所有条件,则声明符缺少初始化程序的局部变量实际上是最终变量:
它没有被宣布为最终的。
当它出现在赋值表达式的左边时,它肯定是未赋值的,也不是绝对赋值的 分配前;也就是说,它肯定是未分配的,而不是 肯定在赋值右侧之后赋值 表达式(§16(明确赋值))。
它永远不会作为前缀或后缀递增或递减运算符的操作数出现。
出于
确定它是否有效最终,作为局部变量
其声明器有一个初始化器。
我的阅读是,在第 2 条中,try/catch 块中的赋值是允许的,因为 fileSize 在赋值之前肯定是未赋值的。
我认为解释拒绝代码的原因是:
- fileSize 在 try 块之前肯定是未分配的
- fileSize 是在fileSize = canThrow() 之后赋值的(肯定的?好像16.1.8 不关心赋值中的异常)
- fileSize 在 try 块之后分配
- fileSize 在 catch 块之前不是绝对未分配的,因此在 catch 块中分配之前也不是绝对未分配的。
- 因此,4.12.4 的第 2 条不适用于此处
这是正确的吗?
【问题讨论】:
-
我会说 fileSize 实际上不是最终的,因为它可以在
fileSize = 0异常或其他情况之后更改。即,如果您将变量设为final,它将无法编译 -
@PeterLawrey 问题是 javac 接受代码,尽管恕我直言它不应该。定义的任何条款都不适用于此处,因此应该被拒绝,尽管我没有看到技术问题,为什么在 lambda 声明后未分配变量时无法完成。删除初始化后,javac 仍然接受代码(恕我直言,这是正确的),但 ecj 仍然拒绝它。
-
我同意这种行为是合理的,尽管不符合规范,这意味着它将来可能会中断。
-
对不起,我编译了错误的文件。 ecj 和 javac 都拒绝该代码,但我不确定这是否符合 JLS。
标签: java lambda language-lawyer final jls