First of all, this is implementation-dependent. The JLS does not specify exactly how a specific snippet or operation must be compiled, as long as the bytecode satistifes the Java Language Specification when run on a VM supporting the Java Virtual Machine specification. A different compiler can generate bytecode that is different from the examples given, as long as it gives the same result when run on a compliant JVM.
On Java 8's javac
(1.8.0_65), the code is not the same for the conditional operator, and the if-else.
The ternary operator controls which value is pushed to the stack, and then the value on the top of the stack is stored unconditionally. In this case, if a>b
, 42 is pushed and code jumps to the istore
, else 59 is pushed. Then whatever value is on top is istore
d to c
.
In the if-else, the conditional controls which istore
instruction is actually called.
Notice however that in both cases the instruction is "compare less than or equal" which jumps to the else branch (continuing the if branch otherwise).
Below can be seen the bytecode generated by various compilers. You can get it yourself using the javap
tool available in an OpenJDK JDK (example command-line javap -c ClassName
)
javac with ternary:
public static void main(java.lang.String...);
Code:
0: sipush 169
3: istore_1
4: sipush 420
7: istore_2
8: iload_1
9: iload_2
10: if_icmple 18
13: bipush 42
15: goto 20
18: bipush 69
20: istore_3
21: return
javac with if-else:
public static void main(java.lang.String...);
Code:
0: sipush 169
3: istore_1
4: sipush 420
7: istore_2
8: iload_1
9: iload_2
10: if_icmple 19
13: bipush 42
15: istore_3
16: goto 22
19: bipush 69
21: istore_3
22: return
}
However, with ecj
, the code is even more odd. Ternary operator conditionally pushes one or the other value, then pops it to discard it (without storing):
Code:
0: sipush 169
3: istore_1
4: sipush 420
7: istore_2
8: iload_1
9: iload_2
10: if_icmple 18
13: bipush 42
15: goto 20
18: bipush 69
20: pop
21: return
ecj
with if-else somehow optimizes out the pushes/stores but still includes an oddball comparison (mind you, there are no side effects to the comparison that need to be retained):
Code:
0: sipush 169
3: istore_1
4: sipush 420
7: istore_2
8: iload_1
9: iload_2
10: if_icmple 13
13: return
When I add a System.out.println(c)
to foil this unused-value discard, I find that the structure of both statements is similar to that of javac
(ternary does conditional push and fixed store, while if-else does conditional store).