I have been using Groovy for a while now. Initially for small scripts, but overtime for bigger tasks. The more I use it, the more I am impressed with it's features. Recently I had to generate a number of detailed reports on the performance of the web applications that make up the system I'm working on. During this process I discovered a whole new appreciation for Closures and, in particular, currying them. A good tutorial on this is here .
To show my readers a practical example of what is possible, here is the class I wrote for tracking the statistics associated with a URI .
package htanalysis
public class UriStat {
// The properties. Groovy will generate the set/get methods.
String uri
BigDecimal totalCpu
BigDecimal totalResponse
BigDecimal totalAccessCount
BigDecimal worstCpu // yes, you can be lazy with ';'s
BigDecimal worstResponse
// Declare closures to get a value from an instance. The value
// may be based on a formulae. To keep things small, using that
// in Groovy:
// - The default argument to a closure is "it"; and
// - Do not need a "return" statement on the last line.
public static final def VALUE_TOTAL_CPU = { it . totalCpu }
public static final def VALUE_TOTAL_RESPONSE = { it . totalResponse }
public static final def VALUE_TOTAL_ACCESSCOUNT = { it . totalAccessCount }
public static final def VALUE_WORST_CPU = { it . worstCpu }
public static final def VALUE_WORST_RESPONSE = { it . worstResponse }
public static final def VALUE_AVG_CPU =
{ it . totalCpu / it.totalAccessCount } / / See a formulae
public static final def VALUE_AVG_RESPONSE =
{ it . totalResponse / it . totalAccessCount }
// This closure compares the values obtained from a two objects
// using the first closure to obtain the values from the object.
// This is to do something spicy.....
private static final def C_GENERIC = { v , o1 , o2 -> v ( o2 ) <=> v ( o1 ) };
// Declare java.util.Comparator instances for comparisons. The
// first bit of magic is to use curry(). This allows you to
// define a closure based on another closure, but fixing the first
// argument to a constant. In this case, the Closure to be used
// to obtain the value for comparison. If it makes you feel better,
// it took a me a bit of time to realise the power of Curry.
//
// The second piece of magic is to use "as Comparator" on the
// end of the Closure to coerce the type to be java.util.Comparator.
public static final Comparator COMP_TOTAL_CPU =
C_GENERIC . curry ( VALUE_TOTAL_CPU ) as Comparator ;
public static final Comparator COMP_TOTAL_RESPONSE =
C_GENERIC . curry ( VALUE_TOTAL_RESPONSE ) as Comparator ;
public static final Comparator COMP_TOTAL_ACCESSCOUNT =
C_GENERIC . curry ( VALUE_TOTAL_ACCESSCOUNT ) as Comparator ;
public static final Comparator COMP_WORST_CPU =
C_GENERIC . curry ( VALUE_WORST_CPU ) as Comparator ;
public static final Comparator COMP_WORST_RESPONSE =
C_GENERIC . curry ( VALUE_WORST_RESPONSE ) as Comparator ;
public static final Comparator COMP_AVG_CPU =
C_GENERIC . curry ( VALUE_AVG_CPU ) as Comparator ;
public static final Comparator COMP_AVG_RESPONSE =
C_GENERIC . curry ( VALUE_AVG_RESPONSE ) as Comparator ;
// Cache the property names. Means that when more properties are
// added to the class, the methods below will take them into account.
// Very nice <-- please say it like Borat. :-)
private static final List < String > PROPNAMES =
UriStat . metaClass . properties . collect ({ it . name })
. findAll ({ it != "class" & amp ;& amp ; it != "metaClass" }). sort ();
// Return a nice formatted string of the property values.
public String toString () {
// Again, you can skip the "return" if you want.
"[" + PROPNAMES . collect ({ "${it}='${this[it]}'" }). join ( ',' ) + "]"
}
// I like default parameters - ah the good old Ada days. :-)
public String csvHeader ( String sep = "," ) {
PROPNAMES . join ( sep )
}
// I could have written the above as
// public String csvHeader(String sep = ",") { PROPNAMES.join(sep) }
public String csvValues ( String sep = "," ) {
return PROPNAMES . collect ({ this [ it ]?: "" }). join ( sep )
}
}