Speech about the priority of operators in Java.

So, more than once I come across such interesting tablets in which operators are exposed in the priority of their execution in the program. A typical example: https://introcs.cs.princeton.edu/java/11precedence/ You can search for other options, but they are all more or less similar. In all the tables that I came across, postfix unary increment and decrement operators have a clear priority over their prefix counterparts. Therefore, one would expect that in a compound expression that contains both the postfix increment / decrement and the prefix one, the exact increment / decrement that was written in postfix form should be initially calculated. Well, then let's look at a small example. 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 this compound expression. The key operation here is the assignment operation, but it will be executed at the very last, as it has the lowest priority. In this case, we need to separately consider the right side of this operation. Obviously, initially it is necessary to perform unary operations, and only then proceed to the binary. But what operation to do first? This is quite important, since the result of the program execution depends on it. If you believe the table, then I must first calculate the increment / decrement, and then move on to their multiplication. Since the postfix form has a higher priority, first I y-- operation y-- , and only then ++y . In general, if you do everything the way I understand it, then the result should be 100 . I am writing this code in my IDE and dumping it to the console. Result: 121 . Why is that? Wrong table of priorities? Or do I not understand something?

    4 answers 4

    The ++y operation is performed immediately before the y value is substituted into the expression.
    Operation y-- right 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 primary expressions exist

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

    postfix expressions

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

    and unary operators

    The operator +, -, ++, -, ~,!, And the type reduction 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 should be assigned a certain level of priority. The JLS does not specifically explain why this was done, justifying itself with "certain ambiguities." It can be assumed that this refers to the convenience of parsing expressions or a specific implementation of its mechanism.

    As it was found out in neighboring answers, it is rather difficult to encounter a conflict between prefix and postfix operators or ambiguity of expression using them, and this separation of priority levels does not greatly affect the practice of writing code.

    • This mechanism is quite clear to me. Here you have described to me the difference between the postfix and prefix notation of increment and decrement operations. I'm interested in a little more. There is a table in which the priority of all operations existing in the Java programming language is written. This priority is used to resolve differences (helps to determine the correct order of operations) in complex composite arithmetic and logical expressions. So, why does the postfix form take precedence over the prefix form, although in practice we don’t see it for some reason? - Lexoid
    • I tried to formulate everything that I dug in UPD. It seems that this is a rudiment or a consequence of some of the features of Java, that postfix operations are moved to a separate level, and not a specially established rule. - Mark

    To figure it out, 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); 
    • Well, but if you follow the priority table, shouldn't the operation y-- in the compound expression z = y * y-- be performed first? - Lexoid
    • not. Here, to better understand how java will understand the expression i+++i ? How i + ++i or i++ + i ? And expressions - from left to right are considered - Oleksiy Morenets

    The value is substituted immediately after the operation. That is, if you execute ++ y, it will return the value of 11 to the place of the operation, and in the place of y-- will first return 11, and then decrease y by one. And it turns out that 11 * 11

    Somehow I didn’t even notice that postfix unary operators have higher priority. They are all in order. Interesting

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

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

    • This is understandable, as it turns out, if you move in a natural direction. But why then even in the official Java textbook from Oracle write such things? What is the catch? If interested, then see for yourself, maybe I just misunderstood something. Here (from the The Java ™ Tutorials section): docs.oracle.com/javase/tutorial/java/nutsandbolts/… - Lexoid
    • I would ask you to pay special attention to the priority table itself (Operator Precedence). - Lexoid
    • @Lexoid I did not understand what bothers you. The description of priorities on the Oracle site and in other places is quite consistent with the answers that you were given, and with the result that you get. - m. vokhm
    • @m. vokhm I can not say that something does not suit me. I understand perfectly how and in what order the unary increment and decrement operations are performed. It just doesn’t fit in with what is presented as an official guide on the Oracle website in The Java ™ Tutorials section. Let's try to figure it out. What is the “operator execution priority” and why is it necessary at all? What is the practical meaning of it? Personally, in my understanding, the situation is approximately as follows. - Lexoid
    • Priority is needed solely to determine the order in which the statements are executed, which themselves are part of a more complex compound arithmetic or logical expression. These are obvious things to even talk about somehow embarrassing. - Lexoid

    Friends, thank you all for your help! Brainstorming really works, I was convinced of this on my own skin. I know that this topic introduces many novice programmers into a stupor, so I decided to paint 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 of execution of statements. In what order does the JVM execute the instructions? Obviously, all instructions (as well as operators of compound expressions) are executed in the left-associative direction we are used to (from left to right), and when we reach the end of the line (as a rule, a special Unicode character is put at the end of each line (or their combination) line terminator (mainly characters that have the mnemonic designation CR + LF )), then we move to the lower line and execute the code from the very beginning in the same left-hand direction (you can think of it as a carriage return to the very beginning of the lines and in a typewriter, and then translation of the same carriage to the beginning of the next line).

    When does the main difficulty begin? All troubles arise at all when we are faced with some uncertainty . What I mean? Let's look at a classic example of how to apply the operator priority table in practice. Take the following arithmetic expression:

     /* * Some code */ int z = a + b * c; 

    From elementary school, we know that multiplicative operations are performed first, and then additive ones, since the former have a clear priority over the latter. How is this implemented in Java itself and what kind of collision can arise here? We see in the right part 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 also one of the operands relative to both operators. It is clear that the final result will differ depending on the chosen path. That's where the priorities of the operators come in! We know perfectly well that the multiplication operation will be performed first, because for us it is very obvious and we don’t even pay attention to it (although all this was originally written in the “brains” of the JVM itself).

    And now I would like to turn to a more complex example, from which it all began. Why does the increment / decrement written in postfix notation take precedence over the prefix form of the same record? So I understood, then in both cases it is possible to give only by one example, where a clear uncertainty may arise. We give these examples:

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

    This form of recording allows only 2 possible options 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 don’t say anything about the form of the record). Here a slightly different kind of uncertainty arises, which differs from the first case we have considered. If in the first case there was a collision at the level of the shared operand, then there is an ambiguity in the question of the unary operator, which can be a postfix / prefix increment / decrement for both variable a and variable b . We have the following options for the disclosure of 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 the subtraction is anti-commutative operations, 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 contact JLS :

    It is not necessary to make a program. There is one exception: If you follow the rules, it is one of the following exceptions: numerical comparison operator>.

    A, A, A, A, A, A, A, A, A, B, A, B, A, B, A, B, A .

    If you’re not a rule, you’ll be looking for a list of shift operator >>>. Worse, it can be ambiguous, for example, as a List , etc.

    As we can understand, at each stage the longest broadcast is used, 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 the lexical translation, especially other examples in which a collision could arise between such operators to come up with something that’s not that difficult, but most likely ( In any case, I personally do not see other possible options for writing the correct program, where I could 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 . Knowing the priority of each of the operators we find the right solution:

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

    What I would like to say at the end? In the global network, you can find a lot of false tables that can be misleading. The authors add there any sort of gag, according to the type of separators, the operator new , operators of type casting and other nonsense. I believe that there is a single source to which you can refer in this matter, this is the official site of Oracle Corporation. And yes, I agree that it is better to use regular brackets to correct priorities! :) Thank you all for your attention! ;)