为啥 Java 中的复合赋值不能捕获溢出问题?
Posted
技术标签:
【中文标题】为啥 Java 中的复合赋值不能捕获溢出问题?【英文标题】:Why doesn't compound assignment in Java catch overflow problems?为什么 Java 中的复合赋值不能捕获溢出问题? 【发布时间】:2011-07-25 18:01:00 【问题描述】:令我震惊的是,以下代码甚至可以在没有警告的情况下编译:
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 求值 只有一次。
这对我来说似乎很荒谬。为什么他们觉得有必要在这里明确投射?似乎自动类型转换无论如何都会处理扩大,并且像这样自动缩小几乎肯定会导致整数溢出。
【问题讨论】:
好问题。对我来说从来没有多大意义。也许 James Gosling 会回应,因为他现在是 Google 员工,应该有很多空闲时间...... @squawknull - 是什么让您认为 Google 员工会在毫无意义的 SO 辩论上浪费他宝贵的“每周一天”? :-) @Stephen C:这就是我今天的笑点。 【参考方案1】:Here 是一种解释:
当您进行赋值时(第一个代码 sn-p),java 会强制执行类型检查,因为 LHS 和 RHS 很可能彼此独立。
但是复合运算符更像 增量运算符。 += 修改变量的值 参与,而不是分配一个新的 变量的值。当你修改 一个字节,你期望一个字节作为 结果。为了让生活更轻松,java 确实 隐式类型转换 复合运算符,因为它们是 修饰符。
【讨论】:
【参考方案2】:此链接已分析您提出的问题。
Varying behavior for possible loss of precision
为避免令人不快的意外,请勿 使用复合赋值运算符 byte、short 或 字符。使用复合赋值时 int 类型变量的运算符, 确保上的表达式 右手边不是长型的, 浮动,或双。使用化合物时 变量的赋值运算符 输入浮点数,确保表达式 右侧不是类型 双倍的。这些规则足以 防止编译器生成 危险的变窄管型。
【讨论】:
【参考方案3】:复合赋值运算符由 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;
【讨论】:
你不认为即使是字节也允许它是危险的吗?如果该代码不是加 1,而是加 128,该怎么办?以上是关于为啥 Java 中的复合赋值不能捕获溢出问题?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 Java 没有条件与和条件或运算符的复合赋值版本? (&&=, ||=)