Search code examples
javacternary

Why does javac not catch this ternary error?


Clearly returning null for a boolean type is an error. Why doesn't javac catch this?

public class Test {
    public static void main(String[] args) {
        Object a = null;
        System.out.println(getThing(a));
    }

    private static boolean getThing(Object o) {
        return o == null ? null : Boolean.TRUE;
    }
}

Solution

  • The question

    Clearly returning null for a boolean type is an error. Why doesn't javac catch this?

    Given that question, here's the relevant part of the code:

    private static boolean getThing(Object o) {
        return o == null ? null : Boolean.TRUE;
    }
    

    That method includes a conditional expression (JLS 15.25) where:

    1. the first expression is o == null
    2. the second expression is null
    3. the third expression is Boolean.TRUE

    Why it compiles

    The reason javac allows it is:

    • The conditional expression is classified as a reference conditional expression (JLS 15.25.3) due to the second and third expressions "null" and "Boolean.TRUE", excerpted from JLS 15.25:

      If both the second and the third operand expressions are boolean expressions, the conditional expression is a boolean conditional expression.

      If both the second and the third operand expressions are numeric expressions, the conditional expression is a numeric conditional expression.

      Otherwise, the conditional expression is a reference conditional expression.

    • The entire conditional expression is classified as type java.lang.Boolean

    • The conditional expression is of reference type Boolean, and the method return type – primitive "boolean" – can be unboxed from Boolean

    The compiler kind of "allowing" a code path where "null" might be unboxed to "boolean" isn't all that different from various other obvious programming errors.

    For example, here's code that creates a new object, gives it a value of null, then tries to invoke a method on that object. This code compiles fine but, unsurprisingly, throws an exception at runtime:

    Object o = null;
    System.out.println(o.hashCode());
    
    Exception in thread "main" java.lang.NullPointerException:
    Cannot invoke "Object.hashCode()" because "o" is null
    

    It compiles, but doesn't run

    Though your code compiles, the following exception is thrown at runtime when null is unboxed to boolean:

    Exception in thread "main" java.lang.NullPointerException:
    Cannot invoke "java.lang.Boolean.booleanValue()"
    

    While the return value of the statement is null – a valid value for Boolean – unboxing to a boolean primitive (to line up with the method return type) throws an exception. Unboxing tries to call null.booleanValue() which won't work.

    You weren't asking, but – an easy fix would be to change the return type of the method from primitive boolean to Boolean. After doing that, your program compiles and runs, printing "null" (from System.out.println(getThing(a)).