Why findAll on null returns empty lists in Groovy

Recently while debugging our grails application I saw something like this:

variable.findAll { it.condition }

Since the debugger told me that this variable was null, I happily thought I might have found my little bug and moved on waiting for the big crash and a nice NullPointerException. Instead I got an empty list.

Wait WAT?

That seemed rather bizarre to me, so I checked it inside a groovy shell.

null.findAll { true } ==> []
null.findAll { false } ==> []
null.findAll { it } ==> []
null.findAll {} ==> []
null.findAll() ==> []

Ok, what’s going on here? First thing to note is that Groovie’s null is an object.

null.getClass().name ==> org.codehaus.groovy.runtime.NullObject

From the Java perspective kind of surprising but since I’m familiar with Ruby it was somehow expectable. So far no magic, but where does that findAll method come from?

null.getMetaClass().methods*.name
==> [equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait, getMetaClass, getProperty, invokeMethod, setMetaClass, setProperty, asBoolean, asType, clone, equals, getNullObject, getProperty, hashCode, invokeMethod, is, iterator, plus, setProperty, toString]

Not there… so Groovy-Voodoo. Luckily there are some developers more experienced with Groovy than me in our company (even one who contributed to it some time ago), so I could ask someone else than Google. We got the source code (Groovy 1.8 in our case) and dug into it. The place where a lot of those magical methods dwell is DefaultGroovyMethods.java. According to the documentation the static methods in this class will be available in the class of each method’s first parameter. So here we found the following:

public static Collection findAll(Object self, Closure closure) {
    List answer = new ArrayList();
    Iterator iter = InvokerHelper.asIterator(self);
    return findAll(closure, answer, iter);
}

Which at least explains why that is available for null. Furthermore it shows us that findAll should work on any Object, too. A quick check in the console confirms this.

new Object().findAll { true } ==> [java.lang.Object@79ad86e9]

However it does not explain how the invocation works and why the result is []. So what’s happening here? The asIterator method simply invokes a method named iterator on self. Groovie’s NullObject defines this particular method in the following way:

public Iterator iterator() {
    return Collections.EMPTY_LIST.iterator();
}

This clearly explains why we get an empty list from our findAll call. In the case of an arbitrary GroovyObject we again find (after an unsuccessful lookup in GroovyObject.java) the iterator method for objects in the DefaultGroovyMethods class simply putting the object into a collection and iterating over it.

public static Iterator iterator(Object o) {
    return DefaultTypeTransformation.asCollection(o).iterator();
}

What is still missing to a full understanding of this phenomenon is how those default groovy methods get invoked. Covering this would be way beyond the scope of this blog post. If you browse around a little in the source all this meta stuff can get kind of overwhelming. What we can take out of this so far (beside getting confused by Groovie’s method invocation mechanisms) is some more awareness to the fact that anything and everything can happen in languages such as Groovy even when it all starts with an innocent null object…

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s