As you might imagine, the NullPointerException
in the first case is as a result of implicit unboxing. This is all as described in JLS Sec 15.25; but the rules are complex, and have some tricky facets.
The surprising thing here is that this:
System.out.println(b ? 0 : null);
Is not the same as
Integer v = null;
System.out.println(b ? 0 : v);
- The conditional expression in the first case has type
Integer
: the 0
is boxed to Integer.valueOf(0)
. Hence, this statement will complete normally, printing 0
or null
.
- The conditional expression in the second case has type
int
: v
is unboxed to v.intValue()
. Hence, this statement will either print 0
, or fail with a NullPointerException
.
So: the first example in the question is actually a combination of these two cases. Inserting the implicit boxing and unboxing, it becomes:
return Integer.valueOf(b1 ? 0 : (b2 ? Integer.valueOf(1) : null).intValue());
which can fail on the call to intValue()
; whereas the second example in the question is:
return b1 ? Integer.valueOf(0) : null;
which will not fail.
If you want to return 0, 1 or null, the smallest change would be to box the 0
explicitly:
return b1 ? Integer.valueOf(0) : b2 ? 1 : null;
This looks awkward, because the 0 and 1 are differently represented. You could box the 1 explicitly too:
return b1 ? Integer.valueOf(0) : b2 ? Integer.valueOf(1) : null;
which works, but is verbose, and some well-meaning colleague (or you, when you come back to it some months later, having forgotten the reasons for the explicit boxing) will remove the boxing, because you don't need that, right?
Personally - having spent time recently looking at the conditional operator in some detail - I would opt to avoid it completely, and not risk falling foul of its curious type rules.
if (b1) {
return 0;
} else if (b2) {
return 1;
} else {
return null;
}