I was testing the precedence between &&
and ||
and I had an example that was confusing. In Java, &&
has higher operator precedence than the operator ||
.
So if we have those 3 expressions:
//expr1 = true , expr2 = false; expr3 = false;
if(expr1 || expr2 && expr3);
It should be evaluated as:
if(expr1 || (expr2 && expr3));
So expr2 && expr3
should be evaluated before expr1
. However, this example:
int a1 = 10;
int a2 = 20;
System.out.println(a1 < a2 || ++a1 > a2 && ++a2 < a1);
System.out.println(a1);
System.out.println(a2);
Outputs:
true
10
20
That proves that only a1 < a2
is evaluated.
Can you explain why this is the case?
The expression is short-circuiting. From the link:
when the first argument of the AND function evaluates to false, the overall value must be false; and when the first argument of the OR function evaluates to true, the overall value must be true.
It sees that the rest of the condition doesn't matter because one of the operands of ||
is already true (10 < 20). If one of the operands is true, then no matter what the rest of the condition is, it's true.
You may use bitwise &
and |
to prevent this.
But, the ( expr2 && expr3 ) should be evaluated before expr1, no ?
No. You have to separate concepts of precedence and evaluation order.
Precedence: Dictates the parenthesization of an expression, not the order in which an expression is evaluated. For an example:
true || false && false
Is parenthesized to this because &&
has higher precedence than ||
:
true || (false && false)
This does not mean that things in parentheses is evaluated first in Java's case. Precedence just clarifies what the operands of an operator are, in this case false
and false
, where as in this case:
(true || false) && (false || false)
The operands for &&
are true
and false
, not false
and false
.
Evaluation Order: Describes in what order each operand is evaluated and operator is applied and is sometimes language specific. This dictates how an expression is evaluated, unlike precedence.
In this case, your example:
true || false && false
As established earlier, becomes this due to precedence:
true || (false && false)
But Java, unlike C++, JavaScript, or a number of other languages has a strictly left to right evaluation. Per the Java Language Specification:
15.7. Evaluation Order
The Java programming language guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right.
15.7.1. Evaluate Left-Hand Operand First
The left-hand operand of a binary operator appears to be fully evaluated before any part of the right-hand operand is evaluated.
So, when you have:
true || (false && false)
Java first evaluates the left operand which turns out to be true
. Then the whole condition short circuits. The right operand of ||
in the parentheses is never evaluated at all. The same goes for your other example:
a1 < a2 || (++a1 > a2 && ++a2 < a1)
^^^^^^^^^^^^^^^^^^^^^^^^
Step 0, precedence and parenthesization
a1 < a2 || (++a1 > a2 && ++a2 < a1)
^^^^^^^
Step 1, left operand evaluated, variables resolved to values 10 and 20, condition is true
true || (++a1 > a2 && ++a2 < a1)
^^^^
Step 2, short circuits, left operand is not evaluated
Take another more complex example:
false || false || true && (false || true) || false
Due to precedence, it becomes:
false || false || (true && (false || true)) || false
Then, evaluation begins, left to right:
false || false || (true && (false || true)) || false
^^^^^^^^^^^^^^
Step 1, false || false, does not short circuit, right operand is evaluated, is false
false || (true && (false || true)) || false
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Step 2, false || (true && (false || true)), does not short circuit, right operand is evaluated
Step 2A, (true && (false || true)), does not short circuit, right operand is evaluated
Step 2B, (false || true), does not short circuit, right operand is evaluated, is true
Step 2C, (true && true), does not short circuit, right operand is evaluated, is true
Step 2D, false || true, does not short circuit, right operand is evaluated, is true
true || false
^^^^
Step 3, true || false short circuits, right operand is not evaluated, is true
Thus the whole expression evaluates to true
. The whole expression was evaluated left to right the whole way through. Precedence only dictated the operands of an operator via parenthesization, not the evaluation order.
Further reading at Eric Lippert's explanatory article on precedence vs associativity vs evaluation order as mentioned by Daniel Pryden, it clears up a lot of the confusion.
The main takeaway is that precedence does not dictate in what an expression is evaluated. It only dictates how an expression should be parenthesized. Evaluation order, on the other hand, tells us exactly how an expression is evaluated, and in Java's case is always left to right.
The first line prints true
because of short-circuiting the ||
operator.
a1 < a2
is true and so the rest of the boolean expression doesn't need to be evaluated and true
is returned.
expr2 should be evaluated before expr1
is incorrect, as operator precedence affects the structure of the expression, rather then the evaluation order (in most cases). If you were to re-order the expression so that it was (expr2 && expr3) || expr1
, then yes, expr2
would be evaluated before expr1
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With