为什么 Java 中的复合赋值不能捕获溢出问题?

令我震惊的是,事实证明,以下代码将在没有警告的情况下编译:

public void test()
{
    int value = 2000000000;
    long increment = 1000000000;
    value += increment;
}

然而,这会产生编译时错误,正如您所期望的那样:

public void test()
{
    int value = 2000000000;
    long increment = 1000000000;
    value = value + increment;
}

我检查了一下,实际上,JLS(第15.26.2节)是这样说的:

形式为 E1 op = E2 的复合赋值表达式等价于 E1 = (T) ((E1) op (E2)),其中 T 是 E1 的类型,只是 E1 只计算一次。

这在我看来是荒谬的。为什么他们觉得有必要明确地在这里投射?似乎自动类型转换无论如何都会处理加宽,并且像这样自动变窄几乎可以保证会导致整数溢出。


答案 1

这里有一个解释:

当您执行赋值(第一个代码片段)时,java会强制执行类型检查,因为 LHS 和 RHS 很可能彼此独立。

但复合算子更像是增量算子。+= 修改所涉及的变量的值,而不是为变量分配新值。修改字节时,需要一个字节作为结果。为了使生活更轻松,java为复合运算符执行隐式类型转换,因为它们是修饰符。


答案 2

复合赋值运算符由 JLS (15.26.2) 指定,如下所示:

“形式为 E1 op= E2 的复合赋值表达式等价于

      E1 = (T)((E1) op (E2))`, 

其中 T 是 E1 的类型,只是 E1 只计算一次。

在这种情况下,E1 的类型 E2 是类型,而 op 是 。所以这相当于:intlong+

value = (int)(value + increment);

添加 a 和 a 会给出 a,然后将其转换回之前的赋值。这一切都很好,因此没有编译错误。intlonglongint

这与简单赋值(即 )之间的区别在于,简单赋值没有类型转换。value = value + increment;


好吧,那么他们为什么这样定义它呢?

我认为原因是要像这样做例子:

    byte b = ...
    b += 1;

如果没有类型转换,将是一个编译错误,您需要将其编写为:b += 1

    b += (byte) 1;

推荐