在 Java 中,当你这样做时
int b = 0;
b = b + 1.0;
您可能会丢失精度错误。但是为什么如果你这样做
int b = 0;
b += 1.0;
没有错误吗?
在 Java 中,当你这样做时
int b = 0;
b = b + 1.0;
您可能会丢失精度错误。但是为什么如果你这样做
int b = 0;
b += 1.0;
没有错误吗?
那是因为b += 1.0;
相当于b = (int) ((b) + (1.0));
. 缩小原语转换 (JLS 5.1.3)隐藏在复合赋值操作中。
E1 op= E2形式的复合赋值表达式等价于E1 = (T)((E1) op (E2)),其中T是E1的类型,除了E1只计算一次。
例如,下面的代码是正确的:
short x = 3; x += 4.6;
并导致
x
具有该值7
,因为它等效于:short x = 3; x = (short)(x + 4.6);
这也解释了为什么以下代码可以编译:
byte b = 1;
int x = 5;
b += x; // compiles fine!
但这不会:
byte b = 1;
int x = 5;
b = b + x; // DOESN'T COMPILE!
在这种情况下,您需要显式转换:
byte b = 1;
int x = 5;
b = (byte) (b + x); // now it compiles fine!
值得注意的是,复合赋值中的隐式强制转换是神奇书籍Java Puzzlers中的Puzzle 9: Tweedledum的主题。以下是本书的部分摘录(为简洁起见,略作编辑):
许多程序员认为这
x += i;
只是x = x + i;
. 这并不完全正确:如果结果的类型比变量的类型宽,则复合赋值运算符会执行无声的缩小原语转换。为避免令人不快的意外,请勿对、或类型的变量使用复合赋值运算符 。对 类型的变量使用复合赋值运算符时,请确保右侧的表达式不是、或类型。对 type 的变量使用复合赋值运算符时,请确保右侧的表达式不是 type 。这些规则足以防止编译器生成危险的窄化强制转换。
byte
short
char
int
long
float
double
float
double
对于语言设计者来说,复合赋值运算符生成不可见的强制转换可能是一个错误;变量的类型比计算结果窄的复合赋值可能是非法的。
最后一段值得注意:C# 在这方面要严格得多(请参阅C# Language Specification 7.13.2 Compound assignment)。