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

时间:2011-03-30 01:21:41

标签: java integer-overflow compound-assignment

令我震惊的是,事实证明以下代码甚至没有警告也会编译:

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   只有一次。

这对我来说似乎很荒谬。为什么他们觉得有必要在这里明确投射?似乎自动类型转换无论如何都会处理扩展,并且像这样自动缩小几乎可以保证导致整数溢出。

3 个答案:

答案 0 :(得分:4)

Here是一个解释:

  

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

     

但复合算子更像是   增量运算符。 + =   修改变量的值   参与,而不是分配新的   变量的值。当你修改   一个字节,你期望一个字节作为   结果。为了让生活更轻松,java确实如此   一个隐式类型转换   复合运算符,因为它们是   改性剂。

答案 1 :(得分:2)

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

  

E1 op= E2形式的复合赋值表达式等同于

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

其中T是E1的类型,但E1只被评估一次。“

在这种情况下,E1的类型为int,E2类型为long,op为+。所以这相当于:

value = (int)(value + increment);

添加intlong会产生long,然后在分配之前将其转换回int。这一切都很好,因此没有编译错误。

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


好的,为什么他们是这样定义的?

我认为原因就是做这样的例子:

    byte b = ...
    b += 1;

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

    b += (byte) 1;

答案 2 :(得分:1)

此链接分析了您提出的问题。

Varying behavior for possible loss of precision

  

为了避免令人不快的意外,请不要   使用复合赋值运算符   byte,short或者类型的变量   焦炭。使用复合赋值时   int类型变量的运算符,   确保表达式   右侧不是长型,   漂浮,或加倍。使用化合物时   赋值运算符的变量   键入float,确保表达式   在右侧不是类型   双。这些规则足以   阻止编译器生成   危险的缩小演员。