Tuesday, June 7, 2011

Groovy's Spread and Spread-Dot Operators

I discuss two convenient Groovy language operators for concisely dealing with elements of Groovy collections in this blog post. In particular, this post focuses on Groovy's spread-dot operator and its spread operator.

Spread-Dot Operator (*.)

In my recent post Ten Groovy One Liners to Impress Your Friends, I implemented the first one liner example from 10 Scala One Liners to Impress Your Friends ("Multiply Each Item in a List by Two") in Groovy as follows:

(1..10).collect{it * 2}

Arturo Herrero, in his post 10 Groovy One Liners to Impress Your Friends, implemented this same functionality differently (at least in appearance) than I did. Here is his implementation:

(1..10)*.multiply(2)

Herrero's example uses the Groovy spread-dot operator (*.). The Groovy spread-dot operator is described in the Operators section of the Groovy User Guide as "equivalent to calling the collect method." In other words, the use of *. is a compact approach to specifying the Collection.collect(Closure) as I used in my example.

Both approaches shown above produce the same result: all items in the collection being acted upon are multiplied by two. The spread-dot operator makes it possible with a small number of characters to apply the same method (multiply in this case) on all entries of the collection. The result both implementations produce is: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20].

There is another difference between the approach that I used and the approach Herrero used. With the explicit Collection.connect(Closure) method, a closure was used to explicitly multiply each entry by two. The spread-dot operator approach did not use an explicit closure, but instead specified the multiply method should be performed on each entry in the collection. The numeric types in the collection supported the multiply method. The spread-dot operator supports field specification and property specification in addition to method specification.

Another example using the spread-dot operator is shown in the next code listing. It is on a collection of strings and a String-specific method, toUpperCase() is used on each element of the collection via the spread-dot operator.

['Denver', 'Cheyenne', 'Reno', 'Sacramento']*.toUpperCase()

When the above is executed and printed out, the results looks like this: [DENVER, CHEYENNE, RENO, SACRAMENTO]. The spread-dot operator allowed toUpperCase() to be applied to each String-based entry in the collection.


Spread Operator (*)

The spread operator is related to the spread-dot operator, but its purpose is to extract entries from a collection and provide them as individual entries. This can be useful for converting collections to individual parameters on method calls. An example should make this clearer. In the next code listing, a Groovy Collection of values destined for a Calendar.set(int, int, int, int, int, int) method is passed to that Calendar method as six arguments via the spread operator.

def calendarInitialValues = [2011, Calendar.JUNE, 7, 14, 30, 21]
def calendar = Calendar.getInstance()
calendar.set(*calendarInitialValues)  // spread operator: six int parameters
println calendar.format("YYYY-MMM-dd hh:mm:ss aa")

The output from running the above example looks like this: 2011-Jun-07 02:30:21 PM

A similar example using the six-argument deprecated constructor provided by Java's Date with the convenience method getDateTimeString() provided by Groovy's GDK Date extension is shown next. It is assumed that the variable calendarInitialValues has been defined as shown in the previous example as a collection of six integers.

println new Date(*calendarInitialValues).dateTimeString

This example leads to this output: 6/7/11 2:30:21 PM

Both of the two examples of the spread operator were applied to the Java classes Date and Calendar. The spread operator works in Groovy code whether it's being used to invoke Groovy or Java methods.


Beware the MissingMethodException

Incorrect use of both the spread-dot operator and the spread operator can lead to the throwing of a MissingMethodException. This exception is typically encountered with the spread-dot operator when the method, field, or property being accessed in conjunction with the spread-dot operator is attempted against an entry that does not support that method, field, or property. For example, if a number without strings had been added to the collection of city name Strings above, this exception would be encountered when the operator attempted to invoke String.toUpperCase() on the numeric type.

The MissingMethodException can also be encountered when using the spread operator if the collection the spread operator is applied to has a different number of arguments than those expected by the method or constructor being invoked. For example, if I had added some arbitrary integers to the end of the six integers intended for the Date/Calendar examples, this exception would have been encountered because none of their respective set methods/constructor expect more than six integers.


Conclusion

Groovy's spread and spread-dot operators can make use of individual items within a collection easier and more concise than explicitly performing the same functionality. In this post, I looked at how the spread-dot operator makes it possible to apply the same method (or access the same field or property) to all entries of a collection and how the spread operator makes it possible to automatically convert a collection to individual pieces for use in code expecting individual pieces rather than an entire collection.

No comments: