It's due to autoboxing and autounboxing. If you look at the bytecode (below), you can see calls to Double.valueOf
(boxing the 3.7d
) and Double#doubleValue
(unboxing the result of the conditional expression). The operands to the conditional operator must be the same type, so the compiler is effectively turning your code into this:
public double getSomeDouble() {
return ("" != null ? Double.valueOf(3.7d) : null).doubleValue();
}
...because Double
is the most specific common type it can find for 3.7d
and null
.
I used a string argument (to eliminate compiler optimization around the invariant expression "" != null
, which the compiler would be able to tell would never be true):
public double getSomeDouble(String str) {
return str != null ? 3.7d : null;
}
which effectively becomes:
public double getSomeDouble(String str) {
return (str != null ? Double.valueOf(3.7d) : null).doubleValue();
}
...and indeed got an NPE at runtime when I passed in null
for str
, when it tried to call doubleValue()
on null
.
Here's the bytecode for my getSomeDouble(String)
(from javap -c MyClass
):
public double getSomeDouble(java.lang.String);
Code:
0: aload_1
1: ifnull 13
4: ldc2_w #7 // double 3.7d
7: invokestatic #9 // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
10: goto 14
13: aconst_null
14: invokevirtual #10 // Method java/lang/Double.doubleValue:()D
17: dreturn