3

I like to use the ternary conditional expression in java programming, but I experienced an issue :

The following code is a small example, but it shows the problem that I have found.

public class Example {

    public Example() {
        Double x = 0.0;    
        A a = new A();   
        x = a == null ? 0.0 : a.getY();   // Happens on this line    
        System.out.println(x);
    }

    class A {
        Double y = null;  
        private Double getY() {
            return y;
        }
    }

    public static void main(String[] args) {
        new Example();
    }

}

What's causing the NullPointerException?

Eran
  • 387,369
  • 54
  • 702
  • 768
Paul
  • 147
  • 1
  • 2
  • 5

2 Answers2

5

This results from the JLS rules of determining the type of the ternary conditional expression :

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.

This rule means that the type of the ternary expression is double and not Double. Unboxing the Double returned by your a.getY() method to double causes NullPointerException, since that method returns null.

a == null ? 0.0   : a.getY();
            double   Double      -> hence the type of the ternary expression is double
Eran
  • 387,369
  • 54
  • 702
  • 768
  • Ok thank you, but I have nullpointerexception not type exception – Paul Jun 23 '16 at 06:59
  • @Paul: Right, but the fundamental cause is the type stuff Eran mentioned: Because the compiler ends up deciding (from the rules) that the third operand should be of type `double`, it automatically unboxes the result of `a.getY()` by inserting a method call (`.doubleValue()`), which throws the NPE. – T.J. Crowder Jun 23 '16 at 07:02
  • @Paul `Unboxing the Double returned by your a.getY() method to double causes NullPointerException` – Eran Jun 23 '16 at 07:04
5

It happens because 0.0 is of type double, not Double. The second two operands to the conditional operator must be of the same type, so autoboxing/unboxing came into it and the compiler turned that code into:

x = Double.valueOf(a == null ? 0.0 : a.getY().doubleValue());
// -^^^^^^^^^^^^^^^--------------------------^^^^^^^^^^^^^^

...which throws because a.getY() returns null, and then the code tries to call doubleValue on null.

If we run javap -c Example to decompile the code, we can see those calls (I've put them in bold):

public class Example {
  public Example();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."":()V
       4: dconst_0
       5: invokestatic  #2                  // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
       8: astore_1
       9: new           #3                  // class Example$A
      12: dup
      13: aload_0
      14: invokespecial #4                  // Method Example$A."":(LExample;)V
      17: astore_2
      18: aload_2
      19: ifnonnull     26
      22: dconst_0
      23: goto          33
      26: aload_2
      27: invokestatic  #5                  // Method Example$A.access$000:(LExample$A;)Ljava/lang/Double;
      30: invokevirtual #6                  // Method java/lang/Double.doubleValue:()D
      33: invokestatic  #2                  // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
      36: astore_1
      37: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
      40: aload_1
      41: invokevirtual #8                  // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
      44: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #9                  // class Example
       3: dup
       4: invokespecial #10                 // Method "":()V
       7: pop
       8: return
}
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875