Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between prefix and postfix ++ operators in Java

There are a few questions regarding this (like Java: Prefix/postfix of increment/decrement operators?) but I'm not asking about the general difference between postfix and prefix ++ operators (I know that part), but about the fundamental difference between them at the Java specification level.

Specifically, is there any difference between prefix and postfix ++ operators other than operator precedence (maybe in the way the javac translates the commands to bytecode or in the way the JVM runs that bytecode)?

For example, would the following code necessarily run the same (in every JVM):

for (int i = 0; i < X; i++) { ... }

And

for (int i = 0; i < X; ++i) { ... }

Is there anything in the JLS that defines that these 2 statements would run in exactly the same way on every platform, Java compiler, JVM, etc., or is it possible (even theoretically) that these 2 statements will run differently?

like image 825
traveh Avatar asked Feb 11 '23 02:02

traveh


2 Answers

Yes, it would run the same. It would compile to the same bytecode, thus JVM will notice no difference whatever JVM it is.

You may check it by yourself:

public class TestIncrement {
    public void testPost(int X) {
        for (int i = 0; i < X; i++) {
            System.out.println(i);
        }
    }

    public void testPre(int X) {
        for (int i = 0; i < X; ++i) {
            System.out.println(i);
        }
    }
}

Both methods are compiled in the same way using either JavaC compiler:

public void testPost(int);
Code:
   0: iconst_0
   1: istore_2
   2: iload_2
   3: iload_1
   4: if_icmpge     20
   7: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
  10: iload_2
  11: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
  14: iinc          2, 1
  17: goto          2
  20: return

Or ECJ compiler:

public void testPost(int);
Code:
  0: iconst_0
  1: istore_2
  2: goto          15
  5: getstatic     #16                 // Field java/lang/System.out:Ljava/io/PrintStream;
  8: iload_2
  9: invokevirtual #22                 // Method java/io/PrintStream.println:(I)V
 12: iinc          2, 1
 15: iload_2
 16: iload_1
 17: if_icmplt     5
 20: return

Different compilers generate different bytecode, but in both cases the bytecode is the same for both methods.

like image 124
Tagir Valeev Avatar answered Feb 13 '23 07:02

Tagir Valeev


The relevant parts of the specification are:

15.15.1. Prefix Increment Operator ++

[…]

At run time, if evaluation of the operand expression completes abruptly, then the prefix increment expression completes abruptly for the same reason and no incrementation occurs. Otherwise, the value 1 is added to the value of the variable and the sum is stored back into the variable. […]
The value of the prefix increment expression is the value of the variable after the new value is stored.

 

15.14.2. Postfix Increment Operator ++

[…]

At run time, if evaluation of the operand expression completes abruptly, then the postfix increment expression completes abruptly for the same reason and no incrementation occurs. Otherwise, the value 1 is added to the value of the variable and the sum is stored back into the variable. […]
The value of the postfix increment expression is the value of the variable before the new value is stored.

So, the only difference is the result value when used in an expression context. When using it in the context of a for loop’s update clause, there relevant part is:

14.14.1.2. Iteration of for Statement

[…]

→ First, if the ForUpdate part is present, the expressions are evaluated in sequence from left to right; their values, if any, are discarded. If evaluation of any expression completes abruptly for some reason, the for statement completes abruptly for the same reason; any ForUpdate statement expressions to the right of the one that completed abruptly are not evaluated.

Taking it literally there would be a difference in the code as these expressions produce different results which are then discarded. However, this difference lies in non-observable behavior and as a consequence, the code is usually compiled to not produce the value in the first place and hence does not differ.

So the answer is, the code may have differences, e.g. when compiled naively, however the observable behavior is guaranteed to be the same.

like image 35
Holger Avatar answered Feb 13 '23 05:02

Holger