我很确信在这里
final int i;
try { i = calculateIndex(); }
catch (Exception e) { i = 1; }
如果控制到达捕获块,我
不可能已经被分配了。但是,Java编译器不同意并声称可能已经分配了最终的局部变量i
。
我在这里是否仍然缺少一些微妙之处,或者这只是Java语言规范用于识别潜在重新分配的模型的弱点?我主要担心的是像Thread.stop()
这样的东西,这可能会导致异常被“凭空”抛出,但我仍然不明白如何在分配后抛出它,这显然是尝试块中的最后一个操作。
如果允许的话,上面的习惯用法会使我的许多方法变得更简单。注意,这个用例在语言中有一流的支持,比如Scala,它始终使用Maybe monad:
final int i = calculateIndex().getOrElse(1);
我认为这个用例提供了一个很好的动机,允许在catch块中明确未分配<code>I
经过一番思考,我更加确定这只是JLS模型的一个弱点:如果我声明公理“在给出的例子中,当控制到达catch-block时,< code>i肯定是未赋值的”,它将不会与任何其他公理或定理冲突。在< code>i在catch-block中赋值之前,编译器不允许对其进行任何读取,因此无法观察到< code>i是否已被赋值。
不太干净(我怀疑你已经在做什么)。但这只是多加了1行。
final int i;
int temp;
try { temp = calculateIndex(); }
catch (IOException e) { temp = 1; }
i = temp;
遗憾的是,我认为JVM是正确的。虽然从代码中可以直观地看出是正确的,但在查看IL的上下文中它是有意义的。我创建了一个简单的run()方法,主要模仿您的情况(此处的简化注释):
0: aload_0
1: invokevirtual #5; // calculateIndex
4: istore_1
5: goto 17
// here's the catch block
17: // is after the catch
因此,虽然您不能轻松地编写代码来测试它,因为它不会编译,但方法的调用、存储值和跳到catch之后是三个独立的操作。您可能(尽管可能性不大)在步骤4和步骤5之间发生异常(Thread.interrupt()似乎是最好的例子)。这将导致在设置了i之后进入catch块。
我不确定您是否可以故意在大量线程和中断的情况下实现这一点(而且编译器无论如何都不会让您编写该代码),但理论上可以设置i,并且您可以进入异常处理块,即使使用这个简单的代码。
JLS狩猎:
如果最终变量被分配给,则是编译时错误,除非它在分配之前肯定未分配(§16)。
第16章引述:
在以下所有条件都成立的情况下,在捕获块之前,V 肯定是未分配的:
在try块之后,v肯定是未赋值的。< br >在属于try块的每个return语句之前,V肯定是未赋值的。< br >在属于try块的form throw e的每个语句中,V在e之后肯定是未赋值的。< br >在try块中出现的每个assert语句后,V肯定没有赋值。< br >在属于try块并且其break目标包含(或者是)try语句的每个break语句之前,V肯定是未赋值的。< br >在属于try块并且其continue目标包含try语句的每个continue语句之前,V肯定是未赋值的。
Bold是我的。在try
块之后,不清楚是否分配了i
。
此外,在该示例中
final int i;
try {
i = foo();
bar();
}
catch(Exception e) { // e might come from bar
i = 1;
}
粗体文本是防止实际错误赋值 i=1
成为非法的唯一条件。因此,这足以证明需要更精细的“绝对未分配”条件才能允许在原始帖子中使用代码。
如果对规范进行了修订,则将该条件替换为:
如果catch块捕获未检查的异常,则在try块之后绝对未分配V。
如果catch块捕获未检查的异常,则在能够抛出由catch块捕获的类型的异常的最后一条语句之前,V绝对未分配。
那么我相信你的代码是合法的。(根据我的临时分析。)
我为此提交了一个JSR,我希望它被忽略,但我很好奇这些是如何处理的。从技术上讲,传真号码是必填字段,我希望如果我在那里输入1-000-000-000,它不会造成太大的损害。
以下代码不能用javac 1.8.0_144和ECJ编译: > 未声明为最终。 在赋值表达式(§15.26)中,它从不作为左手边出现。(请注意,包含 初始值设定项的局部变量声明符不是赋值表达式。) 它从不作为前缀或后缀递增或递减运算符的操作数出现(§15.14,§15.15)。 它从不作为前缀或后缀递增或递减运算符的操作数出现。 方法、构造函数、λ或异常参数(§8.4.1,§8.8.1,§9.4,
因为我相信这是一个很好的编程实践,所以如果我的所有(局部或实例)变量只需要编写一次,我就将它们设为< code>final。 但是,我注意到当变量赋值可以抛出异常时,您不能将所述变量设为最终变量: 有没有办法在不诉诸临时变量的情况下做到这一点?(或者这不是最终修饰符的正确位置?)
问题内容: 我很相信这里 如果控制到达捕获块,则不可能已经分配。但是,Java编译器不同意并主张。 我在这里仍然缺少一些微妙之处,还是这仅仅是Java语言规范用来识别潜在重新分配的模型的弱点?我主要担心的是,可能会导致异常抛出“异常”,但是我仍然看不到分配后如何抛出异常,这显然是try块中的最后一个动作。 如果允许,上述成语会使我的许多方法变得更简单。请注意,该用例在诸如Scala之类的语言中具有
我的代码是这样的: 但最后一句话: 总是停止编译说我需要给新变量分配一个返回值?在if语句之前,已经为k分配了一个值。当我把随机k语句放在if语句中时,它似乎是有效的,但这使得它毫无价值,不是吗?编辑器本身没有错误,但是当我编译时,它给了我这个: 线程“main”java中出现异常。lang.IndexOutOfBoundsException:索引:41,大小:36。util。ArrayList。
问题内容: 如果我有两个线程同时修改结构上的字符串字段,我是否总是会看到分配给该字段的一个或另一个字符串,但没有别的? 问题答案: 否。如果您需要原子操作,则存在。 在转到内存模型将所有相关的细节。在“内存模型”文档的顶部: 修改由多个goroutine同时访问的数据的程序必须序列化此类访问。 要序列化访问,请使用通道操作或其他同步原语(例如和 软件包中的原语)保护数据。