In the following class, the return type of the two methods is inconsistent with the idea that the ternary operator:
return condition?a:b;
is equivalent to
if(condition) {
return a;
} else{
return b;
}
The first returns a Double and the second a Long:
public class IfTest {
public static Long longValue = 1l;
public static Double doubleValue = null;
public static void main(String[] args) {
System.out.println(getWithIf().getClass());// outpus Long
System.out.println(getWithQuestionMark().getClass());// outputs Double
}
public static Object getWithQuestionMark() {
return doubleValue == null ? longValue : doubleValue;
}
public static Object getWithIf() {
if (doubleValue == null) {
return longValue;
} else {
return doubleValue;
}
}
}
I can imagine this has to do with the compiler narrow casting the return type of getWithQuestionMark()
but is that language wise ok? It's certainly not what I would have expected.
Any insights most welcome!
Edit: there's very good answers below. Additionally, the following question referenced by @sakthisundar explores another side effect of the type promotion occurring in the ternary operator: Tricky ternary operator in Java - autoboxing
Java ternary operator is the only conditional operator that takes three operands. It's a one-liner replacement for the if-then-else statement and is used a lot in Java programming. We can use the ternary operator in place of if-else conditions or even switch conditions using nested ternary operators.
If the first operand is true then java ternary operator returns second operand else it returns third operand. Syntax of java ternary operator is: result = testStatement ? value1 : value2; If testStatement is true then value1 is assigned to result variable else value2 is assigned to result variable.
Moreover, as has been pointed out, at the byte code level there's really no difference between the ternary operator and if-then-else.
Basically it's following the rules of section 15.25 of the JLS, specifically:
Otherwise, if the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases:
[...]
Otherwise, binary numeric promotion (§5.6.2) is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands.
So section 5.6.2 is followed, which will basically involves unboxing - so this makes your expression work as if longValue
and doubleValue
were of types long
and double
respectively, and the widening promotion is applied to the long
to get an overall result type of double
.
That double
is then boxed in order to return an Object
from the method.
In addition to @Jon's answer, looking at the bytecode you see:
public static java.lang.Object getWithQuestionMark();
Code:
0: getstatic #7; //Field doubleValue:Ljava/lang/Double;
3: ifnonnull 16
6: getstatic #8; //Field longValue:Ljava/lang/Long;
9: invokevirtual #9; //Method java/lang/Long.longValue:()J
12: l2d
13: goto 22
16: getstatic #7; //Field doubleValue:Ljava/lang/Double;
19: invokevirtual #10; //Method java/lang/Double.doubleValue:()D
22: invokestatic #11; //Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
25: astore_0
26: aload_0
27: areturn
Whereas if you tell the compiler that you're not interested in numbers:
public static Object getWithQuestionMark() {
return doubleValue == null ? (Object)longValue : (Object)doubleValue;
}
you'll get what you were after (bytecode)
public static java.lang.Object getWithQuestionMark();
Code:
0: getstatic #7; //Field doubleValue:Ljava/lang/Double;
3: ifnonnull 12
6: getstatic #8; //Field longValue:Ljava/lang/Long;
9: goto 15
12: getstatic #7; //Field doubleValue:Ljava/lang/Double;
15: areturn
outputs:
$ java IfTest
class java.lang.Long
class java.lang.Long
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