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?
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With