Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ternary expression sometimes bypasses compiler checks validations

Tags:

For some reason, the following code compiles normally:

public double getSomeDouble() {
    return "" != null ? 3.7d : null;
}

I would normally expect Eclipse to mark it as an error (null can not be turned to a double primitive).

Just to back my assumption, this code would not work:

public double getSomeDouble() {
    return null;
}

Eclipse would mark the return null line as error, stating:

Type mismatch: cannot convert from null to double

Why doesn't it say the same thing on the previous code snippet?!

like image 515
Maxim Rahlis Avatar asked Apr 14 '15 09:04

Maxim Rahlis


1 Answers

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
like image 158
T.J. Crowder Avatar answered Sep 19 '22 07:09

T.J. Crowder