Short answer: Change the second null-safe call to a regular call.
I.e. change
obj?.property?.isNotNull
to this:
obj?.property.isNotNull
Long answer:
The docs describe the null-safe operator thusly:
In many situations it is ok for an expression to return null if a
receiver was null
That means the second call in your example, property?.
won't even call isNotNull
if the left side of the call was null
. Instead, it will return null
. So the conditional "effectively" evaluates to:
if (true && null) { // causes NPE when java tries to unbox the Boolean
(By the way - the true
is superfluous in this context, but I'm keeping it just in case you had another condition to check - I'm assuming you're just simplifying it to true
for this example.)
If you make the change I'm suggesting, obj?.property
will be evaluated, then the result will be passed to isNotNull
, evaluating to this:
if (true && isNotNull(null)) {
which returns the proper Boolean
object that will be auto-unboxed as expected.
A Word of Caution
In your first form of isNotNull
, i.e. the one returning primitive boolean
, you should actually get a warning like "Null-safe call of primitive-valued feature isNotNull, default value false will be used".
This is because you're stretching the intent of the null-safe call, which is to return null
without invoking the right side method if the left side of the operator was null
. But if your isNotNull
returns a primitive boolean
, the whole expression obviously can't evaluate to null
, so Xtend uses a default instead, which is false
for booleans.
To emphasize the problem in a different way - it evaluates to false
without calling isNotNull
- that means even if you used a method isNull
after the operator, it would still return false
!
The docs also mention this behavior (albeit in general terms):
For primitive types the default value is returned (e.g. 0 for int).
This may not be what you want in some cases, so a warning will be
raised by default
So I recommend always using a non-primitive return value on the right-hand side of a null-safe call. But if you're going to convert the isNotNull
to a regular call as I suggested, this rule doesn't apply, and either return type is fine.