What are the rules for evaluation order in Java?
Let me say this very clearly, because people misunderstand this all the time:
Order of evaluation of subexpressions is independent of both associativity and precedence. Associativity and precedence determine in what order the operators are executed but do not determine in what order the subexpressions are evaluated. Your question is about the order in which subexpressions are evaluated.
Consider A() + B() + C() * D()
. Multiplication is higher precedence than addition, and addition is left-associative, so this is equivalent to (A() + B()) + (C() * D())
But knowing that only tells you that the first addition will happen before the second addition, and that the multiplication will happen before the second addition. It does not tell you in what order A(), B(), C() and D() will be called! (It also does not tell you whether the multiplication happens before or after the first addition.) It would be perfectly possible to obey the rules of precedence and associativity by compiling this as:
d = D() // these four computations can happen in any order
b = B()
c = C()
a = A()
sum = a + b // these two computations can happen in any order
product = c * d
result = sum + product // this has to happen last
All the rules of precedence and associativity are followed there -- the first addition happens before the second addition, and the multiplication happens before the second addition. Clearly we can do the calls to A(), B(), C() and D() in any order and still obey the rules of precedence and associativity!
We need a rule unrelated to the rules of precedence and associativity to explain the order in which the subexpressions are evaluated. The relevant rule in Java (and C#) is "subexpressions are evaluated left to right". Since A() appears to the left of C(), A() is evaluated first, regardless of the fact that C() is involved in a multiplication and A() is involved only in an addition.
So now you have enough information to answer your question. In a[b] = b = 0
the rules of associativity say that this is a[b] = (b = 0);
but that does not mean that the b=0
runs first! The rules of precedence say that indexing is higher precedence than assignment, but that does not mean that the indexer runs before the rightmost assignment.
(UPDATE: An earlier version of this answer had some small and practically unimportant omissions in the section which follows which I have corrected. I've also written a blog article describing why these rules are sensible in Java and C# here: https://ericlippert.com/2019/01/18/indexer-error-cases/)
Precedence and associativity only tell us that the assignment of zero to b
must happen before the assignment to a[b]
, because the assignment of zero computes the value that is assigned in the indexing operation. Precedence and associativity alone say nothing about whether the a[b]
is evaluated before or after the b=0
.
Again, this is just the same as: A()[B()] = C()
-- All we know is that the indexing has to happen before the assignment. We don't know whether A(), B(), or C() runs first based on precedence and associativity. We need another rule to tell us that.
The rule is, again, "when you have a choice about what to do first, always go left to right". However, there is an interesting wrinkle in this specific scenario. Is the side effect of a thrown exception caused by a null collection or out-of-range index considered part of the computation of the left side of the assignment, or part of the computation of the assignment itself? Java chooses the latter. (Of course, this is a distinction that only matters if the code is already wrong, because correct code does not dereference null or pass a bad index in the first place.)
So what happens?
- The
a[b]
is to the left of theb=0
, so thea[b]
runs first, resulting ina[1]
. However, checking the validity of this indexing operation is delayed. - Then the
b=0
happens. - Then the verification that
a
is valid anda[1]
is in range happens - The assignment of the value to
a[1]
happens last.
So, though in this specific case there are some subtleties to consider for those rare error cases that should not be occurring in correct code in the first place, in general you can reason: things to the left happen before things to the right. That's the rule you're looking for. Talk of precedence and associativity is both confusing and irrelevant.
People get this stuff wrong all the time, even people who should know better. I have edited far too many programming books that stated the rules incorrectly, so it is no surprise that lots of people have completely incorrect beliefs about the relationship between precedence/associativity, and evaluation order -- namely, that in reality there is no such relationship; they are independent.
If this topic interests you, see my articles on the subject for further reading:
http://blogs.msdn.com/b/ericlippert/archive/tags/precedence/
They are about C#, but most of this stuff applies equally well to Java.
Confusing example about '&&' and '||' precedence
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
andfalse
, where as in this case:(true || false) && (false || false)
The operands for
&&
aretrue
andfalse
, notfalse
andfalse
.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.
Why is the operator precedence not followed here?
The expression (++y * (y++ + 5)); will be placed in a stack something like this:
1. [++y]
2. [operation: *]
3. [y++ + 5] // grouped because of the parenthesis
And it will be executed in that order, as result
1. 10+1 = [11] // y incremented
2. [operation: *]
3. 11+5 = [16] // y will only increment after this operation
The the expression is evaluated as
11 * 16 = 176
Differences between Java order of expression, operator precedence and associativity
You are correct, they misspoke, assignment operator has the lowest order of precedence.
You are incorrect, they never mention "order of evaluation" anywhere (that you've shown, anyway). The code shown doesn't do anything where order of evaluation matters. The assignment has nothing to with order of evaluation.
- The code does not compile because the assignment operator has the highest order of precedence in this expression.
Operator precedence shows:
9 > relational
2 ?: ternary
1 = assignment
Which means that to explicitly show precedence using parenthesis, the statement becomes:
statement = ((250 > 338) ? lion : tiger) = " is Bigger";
- Both sides of the ternary operator must have the same type. This expression is invalid, as the left side of the second assignment operator is not a variable, so the answer is option F.
The ternary operator being ((250 > 338) ? lion : tiger)
, "both sides" refer to the two assignment operators.
As it says, "This expression is invalid, as the left side of the second assignment operator is not a variable".
- Note that if the question had added explicit parentheses around the expression (Tiger = " is Bigger"), option E would have the correct output.
You already confirmed that yourself.
To explicitly show precedence using parenthesis, the statement becomes:
statement = ( (250 > 338) ? lion : (tiger = " is Bigger") );
Java operator precendece confusion && and ++
Edit: I missed the point of the question. See revisions for semi-detailed explanation of short-circuit evaluation.
Each operand of the logical-AND and logical-OR operators is a separate expression, and thus, treated independently (given its own order of operators to evaluate, according to correct operator precedence).
So, while ++
has the highest precedence in ++count > 1
, that expression is never even evaluated (because of the short-circuiting).
precedence operator in java 8 - postfix operator first
When do I use the Operator precedence on the same line in an expression? or why there is an operator precedence order and when is used?
Operator precedence decides which one of several operators is associated with an operand. In the expression ++x - x++
there are two places where precedence comes into play:
++x - …
- The two operators++
and (binary)-
could be used onx
;++
has precedence, so this is equivalent to(++x) - …
, not to++(x - …)
.… - x++
- The two operators (binary)-
and++
could be used onx
;++
has precedence, so this is equivalent to… - (x++)
, not to(… - x)++
.
Java operator precedence guidelines
As far as the "Real World" is concerned, it's probably fair to say:
- enough programmers know that multiplication/division take precedence over addition/subtraction, as is mathematically the convention
- hardly any programmers can remember any of the other rules of precedence
So, apart from the specific case of */
vs +-
, I'd really just use brackets to explicitly define the precedence intended.
Related Topics
How to Parse Command Line Arguments in Java
Recursively List Files in Java
Java Process With Input/Output Stream
How to Convert List≪Integer≫ to Int[] in Java
Declaring Variables Inside or Outside of a Loop
Swing: Link Toggle Buttons Together With a Button Group, Along With Corresponding Menu Items
How to Determine Day of Week by Passing Specific Date
How to Split a String by Space
Take a Char Input from the Scanner
Try/Catch With Inputmismatchexception Creates Infinite Loop
Why Do This() and Super() Have to Be the First Statement in a Constructor
How to List the Files Inside a Jar File
"Missing Return Statement" Within If/For/While
String Is Immutable. What Exactly Is the Meaning
Get Generic Type of Java.Util.List
Loading and Displaying Large Text Files