Groovy Spread Operator Optional for Properties (Plus: A Peek Into the Sausage Factory)

| Comments

So groovy has this cool operator called the “spread” operator: “*.”.

Right now, it’s listed as the top hidden feature of Groovy over on StackOverflow. It works like this:

[1, 2, 3, 4]*.toString() 
// equals ["1", "2", "3", "4"]

It applies the method/property to the right of the operator to each member of the collection and returns the results as another collection. It’s syntactic sugar for doing this:

[1, 2, 3, 4].collect { item -> item.toString() }

(and that’s syntactic sugar for a whole pile of Java code :)

Did you know that for properties, you don’t actually need to use the spread operator? Neither did I till I fat-fingered a command working with a list of Grails domain objects and it still worked.

class Book {
    String title       

list = [new Book(title:"foo"), new Book(title: "bar"), new Book(title: "baz") ]

println list.title 

// prints ["foo", "bar", "baz"]

WTF? I forgot the “*”? I should have had to do println list*.title How did that work!?

I played around with it a little more and found that it only worked with properties and not with methods.

[" foo ", " bar ", " baz "]*.trim()
// Works! result: ["foo", "bar", "baz"]

[" foo ", " bar ", " baz "].trim()
// Blows up!  
// Throws MissingMethodException: No signature of method: java.util.ArrayList.trim()

Our Window Into the Sausage Factory: javap

There’s some magic going on here. I don’t like magic, I like to understand what’s going on behind the scenes, even if I have to get a little dirty to figure things out.

Luckily, groovy compiles into .class files that run on the JVM. That means that we have all of the same tools to peek inside the sausage factory as any other Java program does.

If you haven’t used javap previously, it’s a java class disassembler that lets you inspect a class file to see its interface. It comes with java and you likely already have it on your path if you have java installed.

If you have a simple java class called “”:

class Book {
    String title;

You can use javap to inspect the generated class file:

% javap Book      
Compiled from ""
class Book extends java.lang.Object{
    java.lang.String title;

If you do the same thing in groovy:

class Book {
    String title

You can compile that class with:

groovyc Book.groovy

Then you can use javap to inspect the generated class file:

% javap Book       

Compiled from "Book.groovy"
public class Book extends java.lang.Object implements groovy.lang.GroovyObject{
    public static final java.lang.Class $ownClass;
    public static java.lang.Long __timeStamp;
    public static java.lang.Long __timeStamp__239_neverHappen1230094213615;
    public Book();
    protected groovy.lang.MetaClass $getStaticMetaClass();
    public groovy.lang.MetaClass getMetaClass();
    public void setMetaClass(groovy.lang.MetaClass);
    public java.lang.Object invokeMethod(java.lang.String, java.lang.Object);
    public java.lang.Object getProperty(java.lang.String);
    public void setProperty(java.lang.String, java.lang.Object);
    static {};
    public java.lang.String getTitle();
    public void setTitle(java.lang.String);
    public void super$1$finalize();
    public int super$1$hashCode();
    public void super$1$wait();
    public void super$1$wait(long);
    public java.lang.String super$1$toString();
    public void super$1$wait(long, int);
    public java.lang.Class super$1$getClass();
    public void super$1$notify();
    public boolean super$1$equals(java.lang.Object);
    public java.lang.Object super$1$clone();
    public void super$1$notifyAll();
    static java.lang.Class class$(java.lang.String);

Groovy puts quite a bit more information than a java class, lots of it to do with all of the meta-class magic.

If you want to see the actual bytecode of the generated file, you can use the “-c” switch for javap.

We can use that bytecode information to figure out how groovy is applying a property to each item in an array and collecting the results.

I put together these test classes in Test.groovy:

class Book {
    String title

class Test {
    def bookList = [new Book(title:"bar"), new Book(title: "baz"), new Book(title: "quux")]

    def propertyWithoutSplat() {

Then I compiled them with groovyc and ran javap on it. The results are quite long for the entire file, but here is the bytecode for our target propertyWithoutSplat method:

javap -c Test 
public java.lang.Object propertyWithoutSplat();
   0:   invokestatic    #26; //Method $getCallSiteArray:()[Lorg/codehaus/groovy/runtime/callsite/CallSite;
   3:   astore_1
   4:   aload_1
   5:   ldc #85; //int 3
   7:   aaload
   8:   aload_0
   9:   getfield    #59; //Field bookList:Ljava/lang/Object;
   12:  invokeinterface #89,  2; //InterfaceMethod org/codehaus/groovy/runtime/callsite/CallSite.callGetProperty:(Ljava/lang/Object;)Ljava/lang/Object;
   17:  areturn
   18:  goto    25
   21:  invokestatic    #80; //Method org/codehaus/groovy/runtime/ScriptBytecodeAdapter.unwrap:(Lgroovy/lang/GroovyRuntimeException;)Ljava/lang/Throwable;
   24:  athrow
   25:  nop
  Exception table:
   from   to  target type
     0    21    21   Class groovy/lang/GroovyRuntimeException

You don’t need to know too much about what the actual bytecode instructions mean to figure out what’s actually happening with the help of the comments. Here are the lines with real meat:

9:  getfield    #59; //Field bookList:Ljava/lang/Object;
12: invokeinterface #89,  2; //InterfaceMethod org/codehaus/groovy/runtime/callsite/CallSite.callGetProperty:(Ljava/lang/Object;)Ljava/lang/Object;

It’s getting the bookList field and then executing CallSite.callGetProperty on that.

Now we know the methods being called, it’s only a matter of tracking them down in the groovy code base.

If you look at the groovy source, you’ll see that CallSite.callGetProperty leads us to InvokerHelper.getProperty(Object, String):

   public static Object getProperty(Object object, String property) {
       if (object instanceof GroovyObject) {
           GroovyObject pogo = (GroovyObject) object;
           return pogo.getProperty(property);
       } else if (object instanceof Class) {
           Class c = (Class) object;
           return metaRegistry.getMetaClass(c).getProperty(object, property);
       } else {
           return metaRegistry.getMetaClass(object.getClass()).getProperty(object, property);

Which leads us to MetaClassImpl.getProperty(Object, String) which has a section for special cases (like our Collection):

// special cases
       } else if (object instanceof Collection) {
           return DefaultGroovyMethods.getAt((Collection) object, name);
       } else if (object instanceof Object[]) {
           return DefaultGroovyMethods.getAt(Arrays.asList((Object[]) object), name);

Which leads us back to the source of all goodness in the Groovy language: DefaultGroovyMethods.

We’ve tracked the magic down to the DefaultGroovyMethods.getAt(Collection, String):

   public static List getAt(Collection coll, String property) {
       List answer = new ArrayList(coll.size());
       for (Iterator iter = coll.iterator(); iter.hasNext();) {

           Object item =;
           Object value;
           try {
               value = InvokerHelper.getProperty(item, property);
           } catch (MissingPropertyExceptionNoStack mpe) {
               String causeString = new MissingPropertyException(mpe.getProperty(), mpe.getType()).toString();
               throw new MissingPropertyException("Exception evaluating property '" + property +
                       "' for " + coll.getClass().getName() + ", Reason: " + causeString);
       return answer;

It iterates through the collection, grabs the value of the property for each and returns that as a new ArrayList. So whenever we ask a collection for a property, groovy wires things up so that you don’t even need the splat.

It would also be possible to do this kind of detective work with a debugger and stepping through the code. I find that I often learn quite a bit along the way if I try to dig into things myself. When you use a debugger, you often blaze right past all kinds of interesting details that you might learn on the journey.

Using javap is kind of like taking the back roads on your driving trip. You won’t get there as fast as the interstate, and you might get lost along the way. But, there’s a good chance that you’ll see all kinds of things that you’d miss taking the express route.

The next time your run into some “magic” in groovy, spend a little time digging into the details to see what you can learn along the way.