Operation priorities in the Java programming language


This is about operator precedence in Java.

So, more than once I come across such interesting signs in which operators are set in the priority of their execution in the program. Typical example: https://introcs.cs.princeton.edu/java/11precedence/ You can look for other options, but they are all more or less similar. In all the tables I've seen, the postfix unary increment and decrement operators have explicit precedence over their own prefix analogues. Therefore, it would be expected that in a compound expression that contains both a postfix increment/decrement and a prefix, the exact increment/decrement that was written in the postfix form should initially be calculated. Okay, let's look at a small example then. Let's take the following piece of code:

 int y = 10;
 int z = ++y * y--;
 System.out.println(z);

What do we have here? I see as many as 4 operations in a given compound expression. The key operation here is the assignment operation, but it is it will be executed in the last place, because it has the lowest priority. In this case, we need to consider the right side of this operation separately. It is obvious that initially you need to perform unary operations, and only then move on to binary. But which operation should I do first? This is quite fundamental, since the result of the program execution depends on it. If you believe the table, then first I have to calculate the increment/decrement, and then go to their multiplication. Since the postfix form has a higher priority, I perform the y-- operation first, and then ++y. In general, if everything is done as I understand it, then the result should be 100. I write this code in my IDE and output it to the console. Result: 121. Why is that? Is the priority table painted incorrectly? Or is there something I don't understand?

Author: Kromster, 2018-11-30

4 answers

The ++y operation is performed immediately before the value y will be substituted into the expression.
Operation y-- immediately after.

Example 1:

y= 10
z = ++y * y--
----------------------------
y = y + 1
z = 11 * 11 // 11*11 = 121
y = y - 1

Example 2:

z = (y++) * (++y)
----------------------------   
z = 10 * (++y)
y = y + 1
y = y + 1
z = 10 * 12 // 10*12 = 120

UPD: JLS states that there are primary expressions

Primary expressions include most of the simplest types of expressions that others are built from: literals, object creation, field accesses, method calls, and method references and accesses to the array. The expression in parentheses is also considered syntactically as primary expression.

Postfix expressions

Postfix expressions include the use of the postfix ++ and -operators. They are not considered primary expressions (§15.8), but are handled separately in the grammar to avoid certain ambiguities. And become interchangeable only here, at the postfix priority level. expressions.

And unary operators

Operator +, -, ++, --, ~, !, and the type conversion operator (§15.16) are called unary operators.

It can be seen that postfix operations occupy a separate position in the structure of Java expressions, somewhere between method calls and unary operators. Accordingly, they must be assigned a certain level of priority. JLS does not explain specifically why this was done, making excuses for " certain ambiguities". We can assume that this refers to the convenience of parsing expressions or a certain implementation of its mechanism.

As we found out in the neighboring answers, it is quite difficult to encounter a conflict of prefix and postfix operators or ambiguity of expressions with their use, and this separation of priority levels does not significantly affect the practice of writing code.

 4
Author: Mark, 2018-12-04 16:41:19

To understand, just open the .class file and see how java decompiled it:

    int y = 10;
    int y = y + 1; // 11
    int z = y * y--; // 11 * 11, потом y = y - 1, но это уже не важно, т.к. `y` больше не используется
    System.out.println("z = " + z);
 3
Author: Олексій Моренець, 2018-12-01 10:31:58

The value is substituted immediately after the operation is performed. That is, after executing ++y, it will return the value 11 to the place of the operation, and at the place y - it will first return 11, and then reduce y by one. And it turns out that 11*11

I somehow did not even pay attention to the fact that postfix unary operators have a higher priority. They are all executed in the order of the queue. Interesting

Why it works like this, it seems to be clear. Priority is needed in case of conflict resolution, when two operators have different priorities they collide on the same level. That is, (a + b*b) it is immediately clear that multiplication must be performed first, and in the case of (a+a == b*b) there is no point in performing the multiplication operation first, going in the order from left to right we get the same result.

The priority of the ++/-- operators would help solve a situation like (++a--), indicating that it is necessary to reduce the variable first, but such a construction is prohibited, and it does not wash away much in it. In the case of (++a * a--) will be executed from left to right. due to the fact that unary operations change the variable itself, we can track which operation is actually performed first, although in fact it should not matter. They should be used with caution

 1
Author: Serhii Dikobrazko, 2018-12-01 09:18:36

Friends, thank you all for your help! Brainstorming really works, I've seen it the hard way. I know that this topic puts many novice programmers in a stupor, so I decided to describe my thoughts on this matter. I hope that you will not judge strictly.

So, let's start to understand this issue. What is the main problem of people (including mine) in understanding this topic? And the problem is that we confuse "priority" with the usual order executing statements. In what order does the JVM execute the instructions? Obviously, all instructions (as well as operators of compound expressions) are executed in the usual left-associative direction (from left to right), and when we reach the end of the line (as a rule, at the end of each line, put a special Unicode -a character (or a combination of them), which is a string limiter (basically, these are characters that have the mnemonic designation CR+LF)), then we go on the lower line and execute the code from the very beginning in the same left-hand direction (you can imagine this as returning the carriage to the very beginning of the line in the typewriter, and then translating the same carriage to the beginning of the next line).

When do the main difficulties begin? All the troubles arise exactly when we are faced with some uncertainty . What do I mean by that? Let's look at a classic example of using a priority table in practice operators. Take the following arithmetic expression:

/*
* Some code
*/ 

int z = a + b * c;  

Since elementary school, we have known that multiplicative operations are performed first, and then additive operations, since the former have a clear priority over the latter. How is this implemented in the Java itself and what kind of conflict can occur here? We see on the right side of the assignment operation a compound expression that consists of two binary arithmetic operators and 3 operands. Obviously, we have two possible paths that will intersect in one place. We can do this:

(a + b) * с;

Or so:

a + (b * c);

The collision is that in both cases we capture the variable b, which is simultaneously one of the operands relative to both operators. It is clear that the final result will vary depending on the chosen path. This is where the priorities of the operators come into play! We know perfectly well that the multiplication operation will be performed first, because for us it is very obvious and we do not even pay attention to it (although all this is originally written in the" brains " of the JVM).

And now I would like to move on to a more complex example, with which everything began. Why does the increment / decrement written in postfix notation take precedence over the prefix form of the same notation? As far as I understand, in both cases, there is only one example, where there may be obvious uncertainty. Here are these examples:

a---b;
a+++b;

This form of writing allows only 2 possible variants without a compile-time error. Here you can select 2 operators, one of which will be a binary operator (addition or subtraction), as well as a unary increment/decrement (we are not talking about the form of the record yet). Here a slightly different kind of uncertainty arises, which differs from the first case we have considered. If in the first case there is a collision at the level of the shared operand, then here there is ambiguity in the question of the unary operator, which can be a postfix / prefix increment/decrement, both for the variable a and for the variable b. We have the following options for opening brackets:

(a--)-b;     
a-(--b);

(a++)+b;
a+(++b);

The position of the binary operator is of fundamental importance only in the first case, since subtraction is an anticommutative operation, and addition, on the contrary, is commutative. I would like to add that this priority did not appear at all accidentally. To understand this, you need to refer to JLS:

The longest possible translation is used at each step, even if the result does not ultimately make a correct program while another lexical translation would. There is one exception: if lexical translation occurs in a type context (§4.11) and the input stream has two or more consecutive > characters that are followed by a non-> character, then each > character must be translated to the token for the numerical comparison operator >.

The input characters a--b are tokenized (§3.5) as a, --, b, which is not part of any grammatically correct program, even though the tokenization a, -, -, b could be part of a grammatically correct program.

Without the rule for > characters, two consecutive > brackets in a type such as List> would be tokenized as the signed right shift operator >>, while three consecutive > brackets in a type such as List>> would be tokenized as the unsigned right shift operator >>>. Worse, the tokenization of four or more consecutive > brackets in a type such as List>>> would be ambiguous, as various combinations of >, >>, and >>> tokens could represent the >>>> characters.

As we can understand, at each stage is used the longest broadcast, even if it leads to an incorrect program. Most likely, it is for this reason that the postfix form of unary operators has some priority over the prefix one, this fits into the general logic of lexical translation, especially since it is not that difficult to come up with other examples in which a conflict between such operators could occur, but most likely impossible (in any case, I personally do not see other possible options for writing a correct program, where it would be possible to simulate this situation).

Finally, we can consider an example with logical operators.

boolean bool = a ^ b & c | d;

где a, b, c, d — переменные логического типа.  

To understand which operation will be performed first, it would be nice to look here here. Knowing the priority of each of the operators, we find the correct solution:

boolean bool = ((a ^ (b & c)) | d);

What would you like to say in the end? In the global network, you can find a lot of false tables that can be misleading. The authors add all sorts of " otsebyatinu", by the type of delimiters, the new operator, type conversion operators, and other nonsense. I believe that there is only one source to which you can turn in this matter, this is the official website of the corporation Oracle. And yes, I agree that it is better to use the usual brackets for correcting priorities! :) Thank you all for your attention! ;)

 1
Author: Lexoid, 2018-12-02 18:00:56