Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NullPointerException through auto-boxing-behavior of Java ternary operator

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)?

like image 380
Markus A. Avatar asked Oct 06 '12 21:10

Markus A.


3 Answers

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.
like image 198
Rohit Jain Avatar answered Nov 04 '22 00:11

Rohit Jain


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.

like image 28
CrazyCasta Avatar answered Nov 03 '22 22:11

CrazyCasta


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.

like image 2
Aasmund Eldhuset Avatar answered Nov 04 '22 00:11

Aasmund Eldhuset