为什么 Java 不优化 |= 赋值?

Why does Java not optimize |= assignments?

对于此示例,

t*() 始终 returns true,而 f*() 始终 returns false。

假设我们有以下表达式

if ( f1() || t1() || f2() || t2() ){
    // do stuff        
}

如果是这种情况,JVM 会优化执行并只执行 f1()t1() 因为它 'understands' 无论 f2()t2() yield,满足进入if语句的要求,无需再进行计算

我正在编写一个代码,其中我写了这样的东西:

boolean b = false;
b |= f1(); // A
b |= t1(); // B
b |= f2(); // C
b |= t2(); // D

我的一位同事看到了这个并提到他不确定,但是 Java 可能优化语句 C 和 D,因为 b 总是 true从声明 B 开始,这可能会导致一些问题。

我进行了一些测试,似乎所有测试都已正确执行(这是期望的行为),但我仍然想知道为什么这没有得到优化? 我想他可能是对的,JVM 明白一旦 b 为真,没有 |= 操作会改变它的值。

这更多是关于布尔运算符和按位运算符之间的区别。

|= 复合运算符是按位运算符,这意味着对这两项进行计算。

您可以通过设置一个测试来轻松调试它,其中 b 被分配文字 true,然后 |= 分配给 returns 或者 boolean 值,并且其中有一个断点。

断点会一直触发。

另一方面,"shortcut" 优化仅适用于布尔运算符:||&&

注意:关于复合赋值 here 的一些规范,但我找不到关于 |= 赋值的相关部分。

调用没有得到优化,因为 JLS §15.26.2. Compound Assignment Operators 需要计算右侧表达式。

If the left-hand operand expression is not an array access expression, then:

  • First, the left-hand operand is evaluated to produce a variable. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason; the right-hand operand is not evaluated and no assignment occurs.
  • Otherwise, the value of the left-hand operand is saved and then the right-hand operand is evaluated.

  • ...

从历史上看,short-circuiting 条件运算符(&&||)而非按位运算符(&|)的传统可以追溯到 at至少是 C(但值得注意的是,直到 1999 年,C 才具有明确的布尔类型)。

I'm still wondering why doesn't this get optimized?

因为那样会违反 JLS。

声明

b |= f1();

等同于

b = (boolean)(b | f1());

在上面,JLS 要求 b | f1() 计算如下:

  1. 获取b的值。
  2. 调用f1()并捕获结果值
  3. 对两个值应用 | 运算符。

如果 btrue1[,JLS 不会 允许编译器跳过调用 f1() =53=].

如果你想要那种语义(短路),你需要使用b = b || f1();等等。 (如您所述:b ||= f1() 是语法错误。)


1 - 实际上,在无法观察(在单线程程序中)f1() 调用是否发生的情况下,优化将在理论是允许的。但是您只能通过仔细检查 JIT 编译器发出的 本机代码 来检测优化。它只有在调用完全没有副作用的情况下才会发生。