18

This ugly piece of code does compile but throws NPE if s == null

public static boolean isNullOrEmpty(String s)
{
    return s != null ? s.isEmpty() : null;
}

while this does not (as expected):

public static boolean isNullOrEmpty(String s)
{
    if(s != null)
        return s.isEmpty();
    else
        return null;
}

I know both of them are plainly wrong, but as I found the first piece of code in our sources, I was quite surprised it did compile.

Edit: Here's the relevant part of the JLS from Java 7. I guessed the first statement would apply but the bold one does.

15.25 Conditional Operator ? :

[...]

The type of a conditional expression is determined as follows:

[...]

  • 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.

[...]

  • Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2. The type of the conditional expression is the result of applying capture conversion (§5.1.10) to lub(T1, T2) (§15.12.2.7).
atamanroman
  • 11,607
  • 7
  • 57
  • 81

3 Answers3

16

The first has a ternary operator which has a result type of Boolean. The NPE is converting a null to a boolean.

It is actually something like:

Boolean temp = s != null ? s.isEmpty() : null; //no problems here
return temp; //crash when temp==null

The second is trying to return a wrong type (Object instead of primitive) - and thus does not compile.

VIN
  • 6,385
  • 7
  • 38
  • 77
amit
  • 175,853
  • 27
  • 231
  • 333
  • What determines the return type of a ternary? If it's Boolean, it makes sense and autoboxing occures. If it's boolean, it shouldn't compile as the 2nd example. Am I wrong here? – atamanroman Nov 29 '12 at 12:42
  • @atamanroman: The type of the conditional is explained in [the specs 15.25](http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.25). I believe the last condition applies here: `Otherwise, the second and third operands are of types S1 and S2 respectively. Let T1 be the type that results from applying boxing conversion to S1, and let T2 be the type that results from applying boxing conversion to S2. The type of the conditional expression is the result of applying capture conversion (§5.1.10) to lub(T1, T2) ` – amit Nov 29 '12 at 12:49
  • and you are mistaking about the 2nd example because `null` is not being unboxed to `boolean`, its *static type* is not `Boolean`. If you would have done `Boolean b = null; return b;` - you would get your NPE back instead of compile time error – amit Nov 29 '12 at 12:55
  • So S1 And S2 are both boxed and the ternary returns Boolean with the value null which is unboxed and hence throws NPE? Seems weird but correct. I did not expect any boxing since I'm dealing with primitives only. – atamanroman Nov 29 '12 at 13:12
0

Similar to Tricky Ternary Operator in JAVA

The ternary operator does Autoboxing using rules as quoted in JLS:

Boxing Conversion

This rule is necessary because the conditional operator (§15.25) applies boxing conversion to the types of its operands, and uses the result in further calculations.

The problem is with the Autoboxing in Java. The problem is due to second operand not thrid.

public static boolean isNullOrEmpty(String s)
    {
        return s != null ? null : null;
    }

This code wont compile

JLS for Conditional Operator

Community
  • 1
  • 1
Narendra Pathai
  • 41,187
  • 18
  • 82
  • 120
  • maybe I am not totally clear in my answer but referring the JLS will surely help you. – Narendra Pathai Nov 29 '12 at 12:53
  • `null` is NOT `Boolean`, thus applying boxing on `boolean` does not give you the 3rd operand. The case here is the last one (the last otherwise) as I commented to @atamanroman as a comment to my answer. – amit Nov 29 '12 at 12:58
0

Ternary Operator needs to find out the most appropriate return type for both operands.

Thus, it allows some "polish" of the return type.

As showed in your first code snippet:

public static boolean isNullOrEmpty(String s)
{
    return s != null ? s.isEmpty() : null;
}

s.empty() returns primitive boolean whereas third operand returns null. So the most specific common return type is Boolean. Compiler job is like replace the line by:

return s != null ? s.isEmpty() : (Boolean)null; 

Return type method expects a boolean primitive, so the compiler says: "cool, I just have to unbox my result !". Unfortunately, null isn't unboxable and leads to an ugly NPE.

With your second snippet:

public static boolean isNullOrEmpty(String s)
{
    if(s != null)
        return s.isEmpty();
    else
        return null;
}

No additional "polish" of pre-return type is made since compiler doesn't link both return statements in this code => might be a too hard job for it.

So, in this case, it is logical that the code doesn't even compile since null is NOT associated to Boolean! So, no kind of cast occurs.

To compile it, it should be written as:

public static boolean isNullOrEmpty(String s)
    {
        if(s != null)
            return s.isEmpty();
        else
            return (Boolean)null;
    }

But does not prevent the famous NPE to occur while attempting to unbox ;)

Mik378
  • 21,881
  • 15
  • 82
  • 180