8

我发现了Java 的 +=、-=、*=、/= 复合赋值运算符(好问题 :)),但它有一部分我不太了解。借用那个问题:

int i = 5;
long l = 8;

然后i = i + l;不会编译,但i += l;会编译得很好。

已接受的链接问题的答案指出:

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

这与仅评估一次的异常i += l;相同。i = (int)((i) + (l));i

Along可能(IIRC 甚至可以保证)比 a 长int,因此可以容纳更大范围的值。

鉴于这种情况很容易由于在语句执行期间的某个时间点进行必要的缩小转换而导致数据丢失(r 值表达式评估或赋值),为什么i += l;不是编译时错误或至少是警告?

4

3 回答 3

14

基本上,因为i += l就像编写的一样编译i = (int) (i + l)。向变量和变量添加int值时也有类似的“惊喜” ——赋值运算符有效,而普通加法运算符无效。bytechar

于 2012-01-03T14:06:41.067 回答
8

鉴于这种情况很容易由于在语句执行期间的某个时间点进行必要的窄化转换而导致数据丢失(r-value 表达式评估或赋值),为什么 i += l; 不是编译时错误或至少是警告?

正如您所说,它可能应该是编译时错误或至少是警告。我知道的大多数书籍和教程都x += y;x = x + y;. 老实说,除了一个之外,我不知道在 JLS 的第15.26.2 节复合赋值运算符中提到的任何区别。

Java Puzzlers: Traps, Pitfalls, and Corner Cases的第 2 章(谜题 9)中,作者(Joshua Bloch 和 Neal Gafter)要求您提供声明,x以便i这是一个法律声明:

x += i;

这不是:

x = x + i;

有很多解决方案,包括您在问题中发布的前两行代码。作者警告不要对 , 和 , 类型的变量使用复合赋值运算符,byte并建议在对类型变量使用这些运算符时,应确保 RHS 表达式不是,或。shortcharintlongfloatdouble

他们以以下观察结束(强调我的):

总之,复合赋值运算符会默默地生成强制转换。如果计算结果的类型比变量的类型宽,则生成的类型转换是危险的窄化类型转换。这样的强制转换可以默默地丢弃精度或量级。 对于语言设计者来说,复合赋值运算符生成不可见的强制转换可能是一个错误;变量的类型比计算结果窄的复合赋值可能是非法的。

于 2012-02-16T00:17:40.580 回答
1

这样做的原因是你可以这样做:

byte b = 1;

b += 1;

如果 += 扩展为 b = b + 1,则表达式不会进行类型检查,因为表达式“b + 1”是 int 类型。即使将两个字节相加,java 中的所有整数表达式至少都是 int 类型。

public class Test {
  public static void main(String[] args) {
    byte b = 1;
    byte c = 2;
    byte d = b + c;
  }
}


Test.java:5: possible loss of precision
found   : int
required: byte
    byte d = b + c;
               ^
1 error
于 2012-02-16T09:29:13.317 回答