Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NullPointerException with autoboxing in ternary expression

Run the following Java code:

boolean b = false;
Double d1 = 0d;
Double d2 = null;
Double d = b ? d1.doubleValue() : d2;

Why is there a NullPointerException?

like image 495
Alexander Avatar asked Jul 16 '10 14:07

Alexander


3 Answers

The return type of the conditional expression b ? d1.doubleValue : d2 is double. A conditional expression must have a single return type. Following the rules for binary numeric promotion, d2 is autounboxed to a double, which causes a NullPointerException when d2 == null.

From the language spec, section §15.25:

Otherwise, if the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases: ...

Otherwise, binary numeric promotion (§5.6.2) is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands. Note that binary numeric promotion performs unboxing conversion (§5.1.8) and value set conversion (§5.1.13).

like image 59
Justin Ardini Avatar answered Nov 19 '22 13:11

Justin Ardini


Because the two expressions around : must return the same type. This means Java tries to convert the expression d2 to double. This means the bytecode calls doubleValue() on d2 -> NPE.

like image 27
Aaron Digulla Avatar answered Nov 19 '22 14:11

Aaron Digulla


You should generally avoid mixed type computation; compounding this with ?: conditional/ternary only makes it worse.

Here's a quote from Java Puzzlers, Puzzle 8: Dos Equis:

Mixed-type computation can be confusing. Nowhere is this more apparent than conditional expression. [...]

The rules for determining the result type of a conditional expression are too long and complex to reproduce in their entirety, but here are three key points.

  1. If the second and third operands have the same type, that is the type of the conditional expression. In other words, you can avoid the whole mess by steering clear of mixed-type computation.

  2. If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression of type int whose value is representible in type T, the type of the conditional expression is T.

  3. Otherwise, binary numeric promotion is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands.

Point 3 is applied here, and it resulted in unboxing. When you unbox null, naturally a NullPointerException is thrown.

Here's another example of mixed-type computation and ?: that may be surprising:

    Number n = true ? Integer.valueOf(1) : Double.valueOf(2);

    System.out.println(n); // "1.0"
    System.out.println(n instanceof Integer); // "false"
    System.out.println(n instanceof Double);  // "true"

Mixed-type computation is the subject of at least 3 Java Puzzlers.

In closing, here's what Java Puzzlers prescribes:

4.1. Mixed-type computations are confusing

Prescription: Avoid mixed-type computations.

When using the ?: operator with numeric operands, use the same numeric type for both the second and third operands.


On prefering primitive types to boxed primitives

Here's a quote from Effective Java 2nd Edition, Item 49: Prefer primitive types to boxed primitives:

In summary, use primitives in preference to boxed primitive whenever you have the choice. Primitive types are simpler and faster. If you must use boxed primitives, be careful! Autoboxing reduces the verbosity, but not the danger, of using boxed primitives. When your program compares two boxed primitives with the == operator, it does an identity comparison, which is almost certainly not what you want. When your program does mixed-type computations involving boxed and unboxed primitives, it does unboxing, and when your program does unboxing, it can throw NullPointerException. Finally, when your program boxes primitive values, it can result in costly and unnecessary object creations.

There are places where you have no choice but to use boxed primitives, e.g. generics, but otherwise you should seriously consider if a decision to use boxed primitives is justified.

Related questions

  • What is the difference between an int and an Integer in Java/C#?
  • Why does autoboxing in Java allow me to have 3 possible values for a boolean?
  • Is it guaranteed that new Integer(i) == i in Java? (YES!!!)
  • When comparing two Integers in Java does auto-unboxing occur? (NO!!!)
  • Java noob: generics over objects only? (yes, unfortunately)
  • Why does int num = Integer.getInteger(“123”) throw NullPointerException?
like image 35
polygenelubricants Avatar answered Nov 19 '22 15:11

polygenelubricants