Is this NPE-related behavior expected for Groovy's safe navigation operator?

StackOverflow https://stackoverflow.com/questions/1604650

  •  05-07-2019
  •  | 
  •  

Question

I came across an exception in our code today where trim() throws a NPE when doing something like someString?.toLowerCase().trim(). This got me curious, as I was under the impression that the safe nav op would break immediately upon null rather than continuing to call things in the chain. So tinkering a bit in command line groovy...

groovy> def a = null
groovy> def getB = {
groovy> return 'b'
groovy> }
groovy> def getC = {
groovy> return 'c'
groovy> }
groovy> def var1 = a?.b?.c
groovy> var1

===> null

Which is all fine and dandy. But removed the safe nav op on the b, and...

groovy> def a = null
groovy> def getB = {
groovy> return 'b'
groovy> }
groovy> def getC = {
groovy> return 'c'
groovy> }
groovy> def var2 = a?.b.c
groovy> var2
groovy> go
Caught: java.lang.NullPointerException: Cannot get property: c on null object
        at CommandLine.run(CommandLine.groovy:10)

What's going on here? a is null, so if anything, the NPE should be thrown by b, but that's what the safe nav op is there for. I would have expected execution to stop at this point and return null to var2, but instead it seems the null gets passed down through b, causing c to NPE.

Is my understanding of the safe nav op wrong here, or is this a bug in Groovy? Thanks folks!

Was it helpful?

Solution

The safe nav operator is not like Boolean expression short-circuiting. It doesn't pertain to the entire expression. It only guards the one dot-reference immediately next to it.

For this example:

a?.b.c

it's right to get this:

java.lang.NullPointerException: Cannot get property: c on null object
        at CommandLine.run(CommandLine.groovy:10)

What's going on is that the safe nav operator works as advertised on the first part of the expression, guarding against a possible null value for the "a" variable, and thus keeping any references to a.anything from erroring out with an NPE. So, the "a.b" part of the expression is reduced to null and we now have:

null.c

and, of course, when Groovy continues to evaluate that the lack of a question-mark means there's nothing to stop it from raising an NPE.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top