3

We recently ran into a bug in production that I found to be very interesting. My longtime understanding is that a Java boolean can only be false, or true. However, it seems in a Ternary Operator it can ultimately resolve to null, and it never produced a compile error, and built all the way to production. I'm very suprised the following code does not generate a compile error. Does anyone know why it compiles just fine? Imho it should not compile! The value it ultimately resolves to is a native boolean.

boolean por = (str == null || str.length() == 0) ? null : "true".equalsIgnoreCase(str);
Lino
  • 19,604
  • 6
  • 47
  • 65
  • 1
    `boolean` or `Boolean`? If it's indeed the primitive, you can expect a `NullPointerException` at runtime in the case the expression evaluates to `true`. – ernest_k Apr 07 '22 at 15:14
  • 2
    @ernest_k I think the question is why is it a runtime error and not a compilation error. – Dave Newton Apr 07 '22 at 15:18
  • @DaveNewton I see. – ernest_k Apr 07 '22 at 15:19
  • [This post answers your question completely](https://stackoverflow.com/a/25929074/5761558) but I'm not sure the question is a duplicate. – ernest_k Apr 07 '22 at 15:25
  • For the record, although it does not respond to the question you actually asked, that expression is an abomination. If the intention is that the `null` be interpreted as false, then `String.equalsIgnoreCase()` is all you need. It's ok with `null` arguments (returning false in that case), and of course an empty string is not a special case in the first place. So just plain `boolean por = "true".equalsIgnoreCase(str);` would have saved you a lot of trouble. – John Bollinger Apr 08 '22 at 02:20

1 Answers1

7

Does anyone know why it compiles just fine?

Ultimately, because of autoboxing / autounboxing.

When the compiler processes a ternary expression, it uses the second and third operands to choose the result type of the expression. In your case, the second has null type and the third has (primitive) boolean type. These are not directly compatible, but because of autoboxing, both are compatible with type Boolean. The result of the expression therefore has type Boolean, which does support null.

Because of autounboxing, it is allowed to assign a value of type Boolean to a variable of type boolean, but this will fail with a NullPointerException in the event that the value being assigned is null, just as if you had invoked the booleanValue() method on it. I presume that this is the error you observed in production.

This is one of the gotchas of autoboxing.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • 1
    @user3604456 Very clear. This is ther reason one should use an IDE plugin like SonarLint, or SonarQube on the integration server. – Joop Eggen Apr 07 '22 at 15:32
  • If this is the case, and it was caused by autoboxing then auto-unboxing, then why wasn't the final result that was assigned to por false? how could it possibly be null, when the data type is a primitive "boolean"? It should be true, or false, nothing else. You said it assigns a value of Boolean to boolean, but if it did, then it should have assigned it a value of "false". To me, this is a flaw. – user3604456 Apr 07 '22 at 16:13
  • @user3604456, it is among the characteristics of the wrapper classes such as `Boolean` that, as reference types, they can take the value `null`. It is also among the characteristics of the wrapper classes that attempting to unbox `null` is invalid. There are languages with looser type semantics than Java's that provide a `null` analogue that is falsey, but Java has only one falsey value (`false`) and only one truthy value (`true`). You can consider that as flawed as you want, but it's the way it is. For what it's worth, though, it has legitimate uses, such as implementing ternary logic. – John Bollinger Apr 07 '22 at 17:01