令我震惊的是,事实证明以下代码甚至没有警告也会编译:
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 只有一次。
这对我来说似乎很荒谬。为什么他们觉得有必要在这里明确投射?似乎自动类型转换无论如何都会处理扩展,并且像这样自动缩小几乎可以保证导致整数溢出。
答案 0 :(得分:4)
Here是一个解释:
当你执行一个赋值(第一个代码片段)时,java强制执行类型检查,因为LHS和RHS很可能彼此独立。
但复合算子更像是 增量运算符。 + = 修改变量的值 参与,而不是分配新的 变量的值。当你修改 一个字节,你期望一个字节作为 结果。为了让生活更轻松,java确实如此 一个隐式类型转换 复合运算符,因为它们是 改性剂。
答案 1 :(得分:2)
复合赋值运算符由JLS(15.26.2)指定,如下所示:
“
E1 op= E2
形式的复合赋值表达式等同于
E1 = (T)((E1) op (E2))`,
其中T是E1的类型,但E1只被评估一次。“
在这种情况下,E1的类型为int
,E2类型为long
,op为+
。所以这相当于:
value = (int)(value + increment);
添加int
和long
会产生long
,然后在分配之前将其转换回int
。这一切都很好,因此没有编译错误。
这与简单赋值(即value = value + increment;
)之间的区别在于简单赋值没有类型转换。
好的,为什么他们是这样定义的?
我认为原因就是做这样的例子:
byte b = ...
b += 1;
如果没有类型转换,b += 1
将是编译错误,您需要将其编写为:
b += (byte) 1;
答案 2 :(得分:1)
此链接分析了您提出的问题。
Varying behavior for possible loss of precision
为了避免令人不快的意外,请不要 使用复合赋值运算符 byte,short或者类型的变量 焦炭。使用复合赋值时 int类型变量的运算符, 确保表达式 右侧不是长型, 漂浮,或加倍。使用化合物时 赋值运算符的变量 键入float,确保表达式 在右侧不是类型 双。这些规则足以 阻止编译器生成 危险的缩小演员。