19

Please consider this code snippet:

private static void doSomething(Double avg, Double min, Double sd) {
    final Double testMin;
    if (avg != null) {
        testMin = Math.max(min, avg - 3 * sd);
    } else {
        testMin = min;
    }
    System.out.println("testMin=" + testMin);

    final Double verwachtMin = avg != null ? Math.max(min, avg - 3 * sd) : min;
    System.out.println("verwachtMin=" + verwachtMin);
}

As far as I know (and for what my IDE can tell me), the variables testMin and verwachtMin should be equivalent.

As you might expect, I'd rather write the last 2 lines than the first 7. However, when I pass 3 null values to this method, I get an NPE on the calculation of the verwachtMin variable.

Does anyone know how this can happen? Does the ternary operator evaluate the 2nd part, even when the condition is not true?

(Java version 1.6.0_21)

geronimo
  • 839
  • 1
  • 9
  • 19
  • Related: [Booleans, conditional operators and autoboxing](http://stackoverflow.com/questions/3882095/booleans-conditional-operators-and-autoboxing) – BalusC Mar 09 '11 at 14:01

5 Answers5

24

Try:

final Double verwachtMin = avg != null ? new Double(Math.max(min, avg - 3 * sd)) : min;

or

final Double verwachtMin = avg != null ? Double.valueOf(Math.max(min, avg - 3 * sd)) : min;

The types of the alternate sides of the ternary operator were double and Double, which means that the Double gets unboxed to double, and then on assignment we have a boxing from double to Double. If the value of min is null then the unboxing NPEs.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
  • Thanks, should've known this. – geronimo Mar 09 '11 at 14:09
  • Isn't that actually a bug? As far as I can tell the logical thing would be for Java to promote the double to a Double since the other value is already a Double. I guess it has to behave like this due to specifications/legacy code, but it does not make much sense... – Markus Barthlen Nov 26 '18 at 13:30
  • @MarkusBarthlen You wouldn't want to have values unnecessarily boxed as there is the memory management overhead. Perhaps there could be some kind of target typing rule, but the mix of overloading, target typing and the rest of the type system is somewhat overcomplicated even as it stands. – Tom Hawtin - tackline Nov 26 '18 at 17:38
  • If the other value is a Double, you have to take into account that it could be null. Even if you would be saving memory this way. – Markus Barthlen Nov 27 '18 at 07:38
4

Does the ternary operator evaluate the 2nd part, even when the condition is not true

No - but it evaluates the 3rd part and I think in this case it tries to autounbox min (leading to the NPE) because the return type of Math.max() is primitive double and determines the return type of the entire expression.

Autoboxing/-unboxing is of the devil.

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
1

The problem is caused by autoboxing*, not by the ternary operator. You are using the Double wrapper type rather than the double primitive type. Because Math.max() expects double parameters, not Doubles, there is an invisible call to Double#doubleValue() before the values are passed to Math.max(). However, you can't call a method on a null object - hence the NullPointerException.

Why are you using Double instead of double in the first place? A primitive-type variable (such as a double) simply cannot be null.


*Well, autounboxing, in this case

Matt Ball
  • 354,903
  • 100
  • 647
  • 710
0

Auto-unboxing causes the problem. Similar question has been asked before .. You sould use double instead of Double to solve your problem..

Gursel Koca
  • 20,940
  • 2
  • 24
  • 34
0

Math.max(min, avg - 3 * sd) here min, avg and sd are autounboxed from Double to double which when one of them is null causes an NPE.

Thomas
  • 87,414
  • 12
  • 119
  • 157