I tripped across a really strange NullPointerException
the other day caused by an unexpected type-cast in the ternary operator. Given this (useless exemplary) function:
Integer getNumber() {
return null;
}
I was expecting the following two code segments to be exactly identical after compilation:
Integer number;
if (condition) {
number = getNumber();
} else {
number = 0;
}
vs.
Integer number = (condition) ? getNumber() : 0;
.
Turns out, if condition
is true
, the if
-statement works fine, while the ternary opration in the second code segment throws a NullPointerException
. It seems as though the ternary operation has decided to type-cast both choices to int
before auto-boxing the result back into an Integer
!?! In fact, if I explicitly cast the 0
to Integer
, the exception goes away. In other words:
Integer number = (condition) ? getNumber() : 0;
is not the same as:
Integer number = (condition) ? getNumber() : (Integer) 0;
.
So, it seems that there is a byte-code difference between the ternary operator and an equivalent if-else
-statement (something I didn't expect). Which raises three questions: Why is there a difference? Is this a bug in the ternary implementation or is there a reason for the type cast? Given there is a difference, is the ternary operation more or less performant than an equivalent if
-statement (I know, the difference can't be huge, but still)?
According to JLS: -
The type of a conditional expression is determined as follows:
- If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression.
- If one of the second and third operands is of primitive type T, and the type of the other is the result of applying boxing conversion
(§5.1.7) to T, then the type of the conditional expression is T.
The problem is that:
Integer number = (condition) ? getNumber() : 0;
Forces an unboxing and reboxing of the result of getNumber(). This is because the false part of the ternary (0) is an integer, so it tries to convert the result of getNumber() to an int. Whereas the following does not:
Integer number = (condition) ? getNumber() : (Integer) 0;
This is not a bug, just the way Java chose to do things.
This is how it is supposed to work. The ternary operator is not meant to be equivalent to a regular if
statement. The bodies of if
and else
are statements, while the parts following ?
and :
are expressions, that are required to evaluate to the same type.
Put another way: a = b ? c : d
is not supposed to be equivalent to if (b) a = c; else a = d;
. Instead, b ? c : d
is an expression on its own, and the assignment of its result to a
won't affect the outcome.
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