我发现Java's +=, -=, *=, /= compound assignment operators(好问题:)),但它有一部分我不太明白。借用这个问题:
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
仅评估一次。
long
可能(IIRC甚至可以保证)长于int
,因此可以保留更大范围的值。
鉴于这种情况很容易导致数据丢失,因为在执行语句(r值表达式评估或赋值)期间的某些时刻必须缩小转换,为什么i += l;
不是编译时错误或至少是警告?
答案 0 :(得分:14)
基本上,因为编译i += l
就像编写i = (int) (i + l)
一样。将int
值添加到byte
和char
变量时会出现类似的“意外” - 赋值运算符可以正常工作,而普通加法运算符则不然。
答案 1 :(得分:8)
鉴于这种情况很容易导致数据丢失,因为在执行语句(r值表达式评估或赋值)期间的某些时刻必须缩小转换,为什么i + = l;不是编译时错误或至少是警告?
正如您所说,它应该是编译时错误或至少是警告。我所知道的大多数书籍和教程都以x += y;
作为x = x + y;
的简写。老实说,除了一个JLS的15.26.2 Compound Assignment Operators部分之外,我不知道有任何区别。
在Java Puzzlers: Traps, Pitfalls, and Corner Cases的第2章(谜题9)中,作者(Joshua Bloch& Neal Gafter)要求您提供x
和i
的声明,以便这是一份法律声明:
x += i;
而这不是:
x = x + i;
有很多解决方案,包括您在问题中发布的前两行代码。作者警告不要在byte
,short
和char
类型的变量上使用复合赋值运算符,并建议在类型为int
的变量上使用这些运算符时确保RHS表达式不是long
,float
或double
。
他们总结以下观察结果(强调我的):
总之,复合赋值运算符以静默方式生成强制转换。如果计算结果的类型比变量的类型宽,则生成的转换是危险的缩小转换。这样的演员可以默默地丢弃精确度或幅度。 对于语言设计者来说,复合赋值运算符生成隐形强制转换可能是一个错误。复合赋值,其中变量的类型比计算结果的范围窄,应该是非法的。
答案 2 :(得分: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