Java中的快捷方式“or-assignment”(| =)运算符

Shortcut “or-assignment” (|=) operator in Java

我在爪哇有一个很长的比较,我想知道他们中的一个或多个是否是真的。比较字符串很长,很难读取,所以为了可读性,我将其拆分,然后自动使用快捷运算符|=,而不是negativeValue = negativeValue || boolean

1
2
3
4
5
boolean negativeValue = false;
negativeValue |= (defaultStock < 0);
negativeValue |= (defaultWholesale < 0);
negativeValue |= (defaultRetail < 0);
negativeValue |= (defaultDelivery < 0);

如果任何一个默认值为负数,我希望negativeValue为真。这是有效的吗?它会像我期望的那样吗?我在Sun的网站或StackOverflow上看不到它的相关内容,但Eclipse似乎不存在问题,代码可以编译和运行。

同样,如果我想执行几个逻辑交叉,我可以使用&=而不是&&吗?


|=是用于布尔逻辑运算符|(jls 15.22.2)的复合赋值运算符(jls 15.26.2);不要与条件运算符或||混淆(jls 15.24)。还有对应于布尔逻辑&^的复合赋值版本的&=^=

换言之,对于boolean b1, b2,这两个值是相等的:

1
2
 b1 |= b2;
 b1 = b1 | b2;

逻辑运算符(&|与条件运算符(&&||相比)的区别在于前者不"短路";后者确实短路。即:

  • &|总是评估两个操作数
  • &&||有条件地计算右操作数;只有当右操作数的值可能影响二进制运算的结果时,才计算右操作数。这意味着在以下情况下不计算右操作数:
    • &&的左操作数计算为false
      • (因为无论右操作数的计算结果如何,整个表达式都是false)
    • ||的左操作数计算为true
      • (因为无论右操作数的计算结果如何,整个表达式都是true)

所以回到你最初的问题,是的,这个构造是有效的,虽然|=并不是=||的一个等价快捷方式,但它确实计算出你想要的。由于您使用的|=运算符的右侧是一个简单的整数比较运算,因此|不短路的事实是微不足道的。

有些情况下,需要短路,甚至需要短路,但您的场景不是其中之一。

不幸的是,与其他语言不同,Java没有EDCOX1 29和EDCOX1 30。这是在Java为什么没有条件和条件或操作符的复合赋值版本的问题中讨论的?(&;&;=,=)。


它不是一个"捷径"(或短路)操作员,就像和&;&;那样(如果他们已经知道基于lhs的结果,他们不会评估rhs),但在工作方面它会做你想要做的。

作为区别的一个例子,如果text为空,则此代码将很好:

1
boolean nullOrEmpty = text == null || text.equals("")

然而,这不会:

1
2
3
boolean nullOrEmpty = false;
nullOrEmpty |= text == null;
nullOrEmpty |= text.equals(""); // Throws exception if text is null

(很明显,你可以为这个特定的案例做"".equals(text)--我只是想证明这个原则。)


你只要一句话。它在多行上表示,读起来几乎与您的示例代码完全相同,只是命令性较低:

1
2
3
4
5
boolean negativeValue
    = defaultStock < 0
    | defaultWholesale < 0
    | defaultRetail < 0
    | defaultDelivery < 0;

对于最简单的表达式,使用|可能比||更快,因为即使它避免进行比较,它也意味着隐式使用分支,而且可能比使用分支贵很多倍。


这是一篇老文章,但为了给初学者提供一个不同的视角,我想举个例子。

我认为对于类似的复合操作符最常见的用例是+=。我相信我们都写了这样的东西:

1
2
int a = 10;   // a = 10
a += 5;   // a = 15

这有什么意义?重点是避免样板文件和消除重复的代码。

因此,下一行的操作完全相同,避免在同一行中键入两次变量b1

1
b1 |= b2;


如果它是关于可读性的,我已经有了将测试数据与测试逻辑分离的概念。代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// declare data
DataType [] dataToTest = new DataType[] {
    defaultStock,
    defaultWholesale,
    defaultRetail,
    defaultDelivery
}

// define logic
boolean checkIfAnyNegative(DataType [] data) {
    boolean negativeValue = false;
    int i = 0;
    while (!negativeValue && i < data.length) {
        negativeValue = data[i++] < 0;
    }
    return negativeValue;
}

这段代码看起来更冗长,更容易解释。甚至可以在方法调用中创建数组,如下所示:

1
2
3
4
5
6
checkIfAnyNegative(new DataType[] {
    defaultStock,
    defaultWholesale,
    defaultRetail,
    defaultDelivery
});

它比"比较字符串"更可读,而且还具有短路的性能优势(以数组分配和方法调用为代价)。

编辑:使用varargs参数可以简单地实现更高的可读性:

方法签名为:

1
boolean checkIfAnyNegative(DataType ... data)

电话看起来是这样的:

1
checkIfAnyNegative( defaultStock, defaultWholesale, defaultRetail, defaultDelivery );


尽管对于您的问题来说,它可能是多余的,但guava库与Predicates有一些很好的语法,并对or/and Predicates进行短路评估。

本质上,比较被转换成对象,打包成一个集合,然后迭代。对于或谓词,第一个真正命中从迭代返回,反之亦然。


||逻辑布尔或按位或

|=按位包含或与赋值运算符

=不短路的原因是它执行的是位或非逻辑或。这就是说:

1
C |= 2 is same as C = C | 2

Java操作员教程


1
2
3
4
List<Integer> params = Arrays.asList (defaultStock, defaultWholesale,
                                       defaultRetail, defaultDelivery);
int minParam = Collections.min (params);
negativeValue = minParam < 0;