31

在 Java 中,当你这样做时

int b = 0;
b = b + 1.0;

您可能会丢失精度错误。但是为什么如果你这样做

int b = 0;
b += 1.0;

没有错误吗?

4

1 回答 1

35

那是因为b += 1.0;相当于b = (int) ((b) + (1.0));. 缩小原语转换 (JLS 5.1.3)隐藏在复合赋值操作中。

JLS 15.26.2 复合赋值运算符(JLS 第三版):

E1 op= E2形式的复合赋值表达式等价于E1 = (T)((E1) op (E2)),其中TE1的类型,除了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 。这些规则足以防止编译器生成危险的窄化强制转换。byteshortcharintlongfloatdoublefloatdouble

对于语言设计者来说,复合赋值运算符生成不可见的强制转换可能是一个错误;变量的类型比计算结果窄的复合赋值可能是非法的。

最后一段值得注意:C# 在这方面要严格得多(请参阅C# Language Specification 7.13.2 Compound assignment)。

于 2010-04-23T07:12:44.053 回答