Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Double ternary Integer initiation causes a null pointer

Tags:

java

Why is this fine with x being set to null:

boolean condition1 = false;
Integer x = condition1 ? 1 : null;

And this fine with x being set to 2:

boolean condition1 = false, condition2 = true;
Integer x = condition1 ? 1 : condition2? 2 : null;

But this, where x should be set to null causes a java.lang.NullPointerException

boolean condition1 = false, condition2 = false;
Integer x = condition1 ? 1 : condition2 ? 2 : null;

A solution is to use:

Integer x = condition1 ? (Integer)1 : condition2 ? 2 : null;

But I'm not very clear on why a single ternary operator works fine, but not a double.

like image 840
MarkL Avatar asked Aug 05 '14 09:08

MarkL


1 Answers

(I still think this is a duplicate after you've done a bit of unpacking, but hey...)

Expand the one statement into two:

// Not exactly the same, but close...
Integer tmp = condition2 ? 2 : null;
Integer x = condition1 ? 1 : (int) tmp;

That's not exactly the same because it evaluates condition2 ? 2 : null even when condition1 is false - you could model it with a method call instead, but in the case you're worrying about, both condition1 and condition2 are false.

Now, you may ask why we've got the cast to int here. That's because of JLS 15.25.2:

The type of a numeric conditional expression is determined as follows:

  • ...
  • 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.
  • ...

We have int and Integer, so this matches for T = int... and the result of the "inner" conditional expression is unboxed if necessary... and that's what's causing a problem.

Casting the 1 to Integer changes this so that the type of the outer expression is Integer too (because both the second and third operands then have type Integer) so there's no unboxing.

Note that in our expansion, tmp is an Integer, and that really is the type of the "inner" conditional expression, because the type of the third operand is the null type, not Integer. You can make it fail with just the one conditional too:

Integer bang = false ? 2 : (Integer) null;

Basically, a conditional operator with second and third operands of type int and Integer respectively will perform unboxing of the third operand (and the result is type int), but a conditional operator with second and third operands of type int and null respectively will not unbox, and the result type is Integer.

like image 191
Jon Skeet Avatar answered Nov 07 '22 20:11

Jon Skeet