9

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.

Abimaran Kugathasan
  • 31,165
  • 11
  • 75
  • 105
MarkL
  • 103
  • 1
  • 1
    Basically, you're unboxing the null as an `int`. See the duplicate for more details. – Jon Skeet Aug 05 '14 at 09:46
  • Question with six upvotes ? Duplicate of another ? weird – Santhosh Aug 05 '14 at 09:49
  • 1
    It's certainly closely related. But I don't think that other question fully deals with this case. It certainly requires some deeper analysis to see whether the solutions there actually apply here. So I've voted to re-open. I think @JonSkeet has been just a little too enthusiastic here. – Dawood ibn Kareem Aug 06 '14 at 05:15
  • @DavidWallace: Put it this way: I think if you expand this into multiple statements, you'll end up with one statement which is exactly the situation in the duplicate. – Jon Skeet Aug 06 '14 at 06:45
  • 1
    @sankrish: Why is that odd? I see duplicates with upvotes all the time. – Jon Skeet Aug 06 '14 at 07:06
  • @JonSkeet Might be to your experience Jon . odd to me as i am a novice here :) – Santhosh Aug 06 '14 at 07:08
  • @sankrish: Basically it's a matter of "Just because 6 people haven't seen the duplicate before doesn't mean it doesn't exist" :) – Jon Skeet Aug 06 '14 at 07:15
  • @JonSkeet i agree with you :) thanks for your time – Santhosh Aug 06 '14 at 07:20
  • Yes, @JonSkeet, I see what you're saying, but I don't think it's quite that straightforward; there are definitely a couple of steps required to get from here to there. In my opinion, it's different enough to be a separate question. – Dawood ibn Kareem Aug 06 '14 at 08:24
  • And I've just noticed that you have written an excellent answer. Here, have an upvote to add to your collection. – Dawood ibn Kareem Aug 06 '14 at 08:25

1 Answers1

1

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

Community
  • 1
  • 1
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • What happens in the first example, then? – user2357112 Aug 06 '14 at 06:56
  • @user2357112: That's using the null type, not `Integer`, as the type of the third operand. I've just edited something into the answer, at the bottom - see if that helps. – Jon Skeet Aug 06 '14 at 06:59
  • It helps. Looks like your dupe candidate explains it, too. That's a really weird edge case. I wonder if anything prevented them from designing the language so `false ? 2 : (Integer) null` and `false ? 2 : null` behave the same, or if it was simply a mistake. – user2357112 Aug 06 '14 at 07:05
  • @user2357112: Most of the language is designed so that autounboxing happens when you've got one `int` and one `Integer`, so it's not particularly surprising from that perspective. The language *could* use the null type as `Integer` and unbox then too, but then it would *always* fail, which would be odd... – Jon Skeet Aug 06 '14 at 07:06
  • Looks like it's a choice between two sources of confusion. – user2357112 Aug 06 '14 at 07:10