What are the rules for evaluation order in Java?

I am reading Java text and getting the following code:

int[] a = {4,4}; int b = 1; a[b] = b = 0; 

The author did not give a clear explanation in the text, but the effect of the last line: a[1] = 0;

I'm not sure I understand: how did the assessment go?

+81
java order-of-evaluation operator-precedence
Jul 23 '11 at 13:11
source share
7 answers

Let me say this very clearly, because people do not understand this all the time:

The procedure for calculating subexpressions is independent of associativity and priority . Associativity and priority determine in which order the statements are executed, but do not determine in which order the subexpressions are calculated. Your question is about the order in which subexpressions are evaluated.

Consider A() + B() + C() * D() . Multiplication has a higher priority than addition, and addition is left-associative, so this is equivalent to (A() + B()) + (C() * D()) but knowing that it only says that the first will happen addition to the second addition, and that multiplication will occur before the second addition. He does not tell you in what order A (), B (), C () and D () will be called! (He also does not tell you whether multiplication occurs before or after the first addition.) It would be entirely possible to obey the rules of seniority and associativity by compiling it as follows:

 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 

Here all the rules of priority and associativity are observed - the first addition occurs before the second addition, and multiplication occurs before the second addition. It is clear that we can make calls A (), B (), C () and D () in any order and at the same time follow the rules of seniority and associativity!

We need a rule that is not related to the rules of priority and associativity to explain the order in which subexpressions are evaluated. Corresponding rule in Java (and C #): "subexpressions are evaluated from left to right." Since A () is displayed to the left of C (), A () is calculated first, regardless of the fact that C () is involved in the multiplication, and A () is involved only in addition.

So now you have enough information to answer your question. In a[b] = b = 0 , the associativity rules say that it is a[b] = (b = 0); but this does not mean that b=0 started first! Priority rules say that indexing takes precedence over assignment, but this does not mean that the indexer works before the rightmost assignment .

(UPDATE: an earlier version of this answer had some small and almost unimportant omissions in the next section, which I fixed. I also wrote a blog article describing why these rules are reasonable in Java and C # here: https://ericlippert.com / 2019/01/18 / indexer-error-cases / )

Priority and associativity only tells us that assigning zero to b must occur before assigning a[b] , because assigning zero calculates the value assigned in the indexing operation. Priority and associativity alone do not say anything about whether a[b] is evaluated before or after b=0 .

Again, this is the same as: A()[B()] = C() - all we know is that indexing must be done before assignment. We do not know whether A (), B (), or C () is executed primarily on the basis of priority and associativity. We need another rule to tell us this.

The rule, again, is "when you have a choice of what to do first, always go from left to right." However, there is an interesting crease in this particular scenario. Is a side effect of a thrown exception caused by a collection of zero or an index outside the valid range considered to be part of the calculation of the left side of the destination, or part of the calculation of the destination itself? Java chooses the latter. (Of course, this difference only matters if the code is already incorrect, because the correct code does not dereference zero and does not pass a bad index in the first place.)

So what is going on?

  • A a[b] is to the left of b=0 , so a[b] is first run, which leads to a[1] . However, validation of this indexing operation is delayed.
  • Then b=0 occurs.
  • Then, a check is made that a is valid and a[1] is in the range
  • The assignment of the value of a[1] occurs last.

Thus, although in this particular case there are some subtleties that should be taken into account for those rare cases of errors that should not primarily occur in the correct code, in the general case you can reason: things on the left happen earlier than things on the right . This is the rule you are looking for. Talking about seniority and associativity is confusing and inappropriate.

People are always mistaken, even those who need to know better. I have edited too many programming books in which the rules were incorrectly formulated, so it is not surprising that many people have completely incorrect ideas about the relationship between priority / associativity and the order of evaluation, namely that in reality there are no such relations; they are independent.

If this topic interests you, see My articles on this topic for further reading:

http://blogs.msdn.com/b/ericlippert/archive/tags/precedence/

They are about C #, but most of these things apply equally well to Java.

+165
Jul 23 '11 at 15:34
source share

Eric Lippert expertly answers nonetheless not very helpful, because it is a different language. This is Java, where the Java language specification is the ultimate description of semantics. In particular, §15.26.1 is significant because it describes the evaluation order for the operator = (we all know that it is right-associative, right?). Having edited this a bit, we will take care of this issue:

If the left operand expression is an array access expression ( §15.13 ), many steps must be performed:

  • First, the subexpression of the array reference of the access expression array of the left operand is evaluated. If this evaluation completes abruptly, then the assignment expression abruptly terminates for the same reason; index subexpression (from the expression of access to the left element of the operand) and the right operand are not evaluated and assignment is not performed.
  • Otherwise, the index sub-expression of the left expression of access to the operand element is evaluated. If this evaluation completes abruptly, the assignment expression abruptly terminates for the same reason, and the right operand is not evaluated and no assignment occurs.
  • Otherwise, the right operand is evaluated. If this evaluation completes abruptly, the assignment expression abruptly terminates for the same reason and no assignment occurs.

[... the following further describes the actual meaning of the assignment itself, which we can ignore here for brevity ...]

In short, Java has a very strictly defined evaluation order , which is almost exactly left to right in arguments to any operator or method call. Array assignments are one of the most difficult cases, but even there it is still L2R. (JLS recommends that you not write code that needs these types of complex semantic restrictions, and so do I: you can get more than enough problems with just one assignment per statement!)

C and C ++ are definitely different from Java in this area: their language definitions leave the evaluation order undefined intentionally to provide greater optimization. C # is similar to Java, but I do not know its literature well enough to be able to point to a formal definition. (It really depends on the language, although Ruby is strictly L2R, like Tcl - although it does not have an assignment operator for reasons not relevant here, and Python L2R but R2L with respect to the destination , which I find strange, but there you go .)

+32
Jul 23 2018-11-23T00:
source share
 a[b] = b = 0; 

1) array indexing operator has higher priority than assignment operator (see this answer ):

 (a[b]) = b = 0; 

2) According to 15.26. JLS Assignment Operators

There are 12 assignment operators; they are all syntactically right associative (they are grouped from right to left). So a = b = c means a = (b = c), which assigns the value c to b and then assigns the value b a.

 (a[b]) = (b=0); 

3) According to 15.7. JLS Evaluation Procedure

The Java programming language ensures that operator operands are evaluated in a specific evaluation order, namely, from left to right.

and

The left operand of a binary operator is completely evaluated before any part of the right operand is evaluated.

So:

a) (a[b]) first evaluated a[1]

b), then (b=0) is estimated as 0

c) (a[1] = 0) is evaluated last

+5
07 Oct '15 at 10:11
source share

Your code is equivalent to:

 int[] a = {4,4}; int b = 1; c = b; b = 0; a[c] = b; 

which explains the result.

+1
Jul 23 '11 at 13:18
source share

Here is another example to describe the priority order in Java

Consider the following code example:

 class A{ public static void main(String[] args){ int x = 20; x =x+(x=5); System.out.println(x); }} 

Question: What is the output of x? Try to solve this problem before continuing with the answer.

Answer:

Step 1: the compiler allocates 20 variables x. This is important - all variables are defined first ie

 int x = 20; 

Step 2. The compiler applies the rules of order of priority, that is, the brackets first:

 (x=5); 

Now we have:

 x =20+(x=5); 

Result:

 x =20+5; 

Therefore, the output is 25:

 System.out.println(x); 

Findings:

You must be like this:

1 All variables are defined first in expressions.

2 You know the operator of order of priority and associativity.

0
Nov 16 '16 at 10:48
source share

Consider another more detailed example below.

As a general rule of thumb:

It is best to have a table of Priority Rules and Associativity readable when resolving these issues, for example. http://introcs.cs.princeton.edu/java/11precedence/

Here is a good example:

 System.out.println(3+100/10*2-13); 

Question: What is the output of the above line?

Answer: apply the rules of priority and associativity

Step 1: According to the priority rules: / and * operators take precedence over + - operators. Therefore, the starting point for fulfilling this equation will narrow to:

 100/10*2 

Step 2: According to the rules and priority: / and * are equal in priority.

The operators / and * are equal in priority, we need to look at the associativity between these operators.

In accordance with the ASSOCIATIVITY RULES of these two specific operators, we start the execution of the equation from LEFT TO RIGHT, that is, 100/10 is executed first:

 100/10*2 =100/10 =10*2 =20 

Step 3: The equation is now in the following execution state:

 =3+20-13 

In accordance with the rules and priority: + and - are equal in priority.

Now we need to look at the associativity between the + and - operators. According to the associativity of these two specific operators, we begin the execution of the equation from LEFT to RIGHT, i.e. 3 + 20 is executed first:

 =3+20 =23 =23-13 =10 

10 - correct output at compilation

Again, it is important to have a table of the Rules of Priority and Associativity when dealing with these issues, for example. http://introcs.cs.princeton.edu/java/11precedence/

0
Nov 16 '16 at 17:56
source share
 public class TestClass{   public static void main(String args[] ){      int i = 0 ;      int[] iA = {10, 20} ;      iA[i] = i = 30 ; System.out.println(""+ iA[ 0 ] + " " + iA[ 1 ] + "  "+i) ;    } } 

He will print 30 20 30

Statement iA [i] = i = 30; will be processed as follows:

iA [i] = i = 30; => iA [0] = i = 30; => i = 30; iA [0] = i; => iA [0] = 30;

Here is what JLS says about it:

1 Rate the operand's left hand first
2 Evaluate Operands Before Surgery
3 Assessment of compliance with brackets and priorities
4 argument lists are evaluated left-right

For arrays: first, dimension expressions are evaluated from left to right. If any of the evaluations of an expression completes abruptly, the expressions to the right of it are not evaluated.

-one
03 Feb '17 at 16:33
source share



All Articles