Java 中的快捷“或-赋值”(| =)运算符

我在 Java 中有很多比较要做,我想知道是否有一个或多个比较是正确的。这个比较字符串很长,很难读,所以为了便于阅读,我将其分解,并自动使用快捷操作符 |=而不是 negativeValue = negativeValue || boolean

boolean negativeValue = false;
negativeValue |= (defaultStock < 0);
negativeValue |= (defaultWholesale < 0);
negativeValue |= (defaultRetail < 0);
negativeValue |= (defaultDelivery < 0);

如果默认值 < something > 为负值,那么我希望 negativeValue为真。这有效吗?它能达到我的期望吗?我在 Sun 的站点或 stackoverflow 上没有看到它,但是 Eclipse 似乎没有问题,代码编译并运行。


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

66625 次浏览

It's not a "shortcut" (or short-circuiting) operator in the way that || and && are (in that they won't evaluate the RHS if they already know the result based on the LHS) but it will do what you want in terms of working.

As an example of the difference, this code will be fine if text is null:

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

whereas this won't:

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

(Obviously you could do "".equals(text) for that particular case - I'm just trying to demonstrate the principle.)

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

The |= is a compound assignment operator (JLS 15.26.2) for the boolean logical operator | (JLS 15.22.2); not to be confused with the conditional-or || (JLS 15.24). There are also &= and ^= corresponding to the compound assignment version of the boolean logical & and ^ respectively.

In other words, for boolean b1, b2, these two are equivalent:

 b1 |= b2;
b1 = b1 | b2;

The difference between the logical operators (& and |) compared to their conditional counterparts (&& and ||) is that the former do not "shortcircuit"; the latter do. That is:

  • & and | always evaluate both operands
  • && and || evaluate the right operand conditionally; the right operand is evaluated only if its value could affect the result of the binary operation. That means that the right operand is NOT evaluated when:
    • The left operand of && evaluates to false
      • (because no matter what the right operand evaluates to, the entire expression is false)
    • The left operand of || evaluates to true
      • (because no matter what the right operand evaluates to, the entire expression is true)

So going back to your original question, yes, that construct is valid, and while |= is not exactly an equivalent shortcut for = and ||, it does compute what you want. Since the right hand side of the |= operator in your usage is a simple integer comparison operation, the fact that | does not shortcircuit is insignificant.

There are cases, when shortcircuiting is desired, or even required, but your scenario is not one of them.

It is unfortunate that unlike some other languages, Java does not have &&= and ||=. This was discussed in the question Why doesn't Java have compound assignment versions of the conditional-and and conditional-or operators? (&&=, ||=).

You could just have one statement. Expressed over multiple lines it reads almost exactly like your sample code, only less imperative:

boolean negativeValue
= defaultStock < 0
| defaultWholesale < 0
| defaultRetail < 0
| defaultDelivery < 0;

For simplest expressions, using | can be faster than || because even though it avoids doing a comparison it means using a branch implicitly and that can be many times more expensive.

Though it might be overkill for your problem, the Guava library has some nice syntax with Predicates and does short-circuit evaluation of or/and Predicates.

Essentially, the comparisons are turned into objects, packaged into a collection, and then iterated over. For or predicates, the first true hit returns from the iteration, and vice versa for and.

|| logical boolean OR
| bitwise OR

|= bitwise inclusive OR and assignment operator

The reason why |= doesn't shortcircit is because it does a bitwise OR not a logical OR. That is to say:



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


Tutorial for java operators

If it is about readability I've got the concept of separation tested data from the testing logic. Code sample:

// 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;
}

The code looks more verbose and self-explanatory. You may even create an array in method call, like this:

checkIfAnyNegative(new DataType[] {
defaultStock,
defaultWholesale,
defaultRetail,
defaultDelivery
});

It's more readable than 'comparison string', and also has performance advantage of short-circuiting (at the cost of array allocation and method call).

Edit: Even more readability can be simply achieved by using varargs parameters:

Method signature would be:

boolean checkIfAnyNegative(DataType ... data)

And the call could look like this:

checkIfAnyNegative( defaultStock, defaultWholesale, defaultRetail, defaultDelivery );

It's an old post but in order to provide a different perspective for beginners, I would like give an example.

I think the most common use case for a similar compound operator would be +=. I'm sure we all wrote something like this:

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

What was the point of this? The point was to avoid boilerplate and eliminate the repetitive code.

So, next line does exactly the same, avoiding to type the variable b1 twice in the same line.

b1 |= b2;