13

令我震惊的是,以下代码甚至可以在没有警告的情况下编译:

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

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

4

3 回答 3

4

是一种解释:

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

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

于 2011-03-30T01:47:58.603 回答
2

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

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

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

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

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

value = (int)(value + increment);

anint和 a相加long得到 a long,然后将其转换回int之前的赋值。这一切都很好,因此没有编译错误。

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


好的,那他们为什么要这样定义呢?

我认为原因是要制作这样的示例:

    byte b = ...
    b += 1;

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

    b += (byte) 1;
于 2011-03-30T02:21:40.730 回答
1

这个链接已经分析了你提出的问题。

改变行为可能导致精度损失

为避免令人不快的意外,请勿对 byte、short 或 char 类型的变量使用复合赋值运算符。在对 int 类型的变量使用复合赋值运算符时,请确保右侧的表达式不是 long、float 或 double 类型。对 float 类型的变量使用复合赋值运算符时,请确保右侧的表达式不是 double 类型。这些规则足以防止编译器生成危险的窄化强制转换。

于 2011-03-30T02:01:17.547 回答