Search code examples
javaconditional-operator

Why is Java evaluating the wrong side of ternary operation?


I might miss some simple explanation, but the following line throws a NullPointerException:

Long sum = true 
           ? 
           fiscalDocumentUtil.getFullValue(fiscalDocument.getInvoice()) 
           :
           (long) fiscalDocument.getReceipt().getAmount();

While

Long sum = true 
           ? 
           fiscalDocumentUtil.getFullValue(fiscalDocument.getInvoice()) 
           :
           null

does not. I would also like to mention that

fiscalDocumentUtil.getFullValue(fiscalDocument.getInvoice())

does not throw an exception on its own, while

(long) fiscalDocument.getReceipt().getAmount()

does.

Is the second side evaluated? Or am I butchering something?

EDIT

Some additional info, asked in the comments: fiscalDocumentUtil.getFullValue(fiscalDocument.getInvoice()) returns null. The return type of fiscalDocumentUtil.getFullValue(fiscalDocument.getInvoice()) is Long.

The return type of fiscalDocument.getReceipt().getAmount() is Integer.

If I emit the explicit (long) conversion, the behavior is the same.

Edit 2

A minimal reproducible example: Long val =true ? (Long) null: Integer.valueOf(1);. This was partially suggested by the answer of MC Emperor and partially suggested by comments. Of course, this example already somewhat answers the question, since the second expression does not need to throw an exception, as it turns out.


Solution

  • If foo has type Long and bar has type long, then true ? foo : bar has type long, and is equivalent to true ? foo.longValue() : bar. As the Java Language Specification puts it:

    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.

    (In your case, T is long, and "the result of applying boxing conversion (§5.1.7) to T" is Long.)

    The fact that you try to put the result in a Long variable doesn't change that fact; it just gives you the equivalent of Long.valueOf(true ? foo.longValue() : bar), with unboxing followed by boxing.

    In your case, foo == null, so the unboxing throws a NullPointerException, so the boxing never happens.