Andres is a DZone Zone Leader and has posted 143 posts at DZone. You can read more from them at their website. View Full User Profile

Introduction to Groovy Part 3

01.10.2008
| 6765 views |
  • submit to reddit

In this third installment ofIntroduction to Groovy (part 1, part 2) we will continue looking at some features of the Groovy language. Some you may find them on other languages, but some are exclusive to Groovy.

Varargs

The ability for a method to work with a variable number of arguments is relatively new to Java (since Java5 to be precise). But it has been a feature other language, even before Java was born (in languages like C or LiveScript [extra points for those who actually coded with JavaScript's grand daddy]). Before the current branch for Groovy 1.1 gave us support for the three dot notation available in Java5, Groovy followed a convention if you wanted a method to have varargs: the last argument should be declared as Object[]
Let's see an example of that:
Listing 2.1

 

class Calculator {
// follow the convention, if the argument is of type Object[]
// then the method call may be used with varargs
def addAllGroovy( Object[] args ){
int total = 0
args.each { total += it }
total
}
// Java5 equivalent also available in Groovy 1.1
def addAllJava( int... args ){
int total = 0
args.each { total += it }
total
}
}
Calculator c = new Calculator()
assert c.addAllGroovy(1,2,3,4,5) == 15
assert c.addAllJava(1,2,3,4,5) == 15

 

Before I get slammed with hate-mail I know there are better ways to sum numbers in Groovy, this example is just to show varargs in action.

Named parameters

When POGOs were discussed in the last part we saw a way to create bean instances using a combination of key/value pairs, where each key mapped to a property. The trick (if you recall) is that Groovy injects a generic constructor with a Map as parameter. A similar trick may be used to simulate named parameters in any methods. Just declare a Map as parameter and access its keys inside the method
Listing 3.1

 

class Math {
static getSlope( Map args ){
def b = args.b ? args.b : 0
(args.y - b) / args.x
}
}
assert 1 == Math.getSlope( x: 2, y: 2 )
assert 0.5 == Math.getSlope( x: 2, y: 2, b: 1 )

 

If you remember your math lessons from school, the straight line formula is y = mx + b where m is the slope and b is a constant. When you solve the equation for m you may find the value for any point in the space (x, y) with an optional offset (b). This technique allows you to communicate in a better way what's the meaning of each parameter, but at the same time sacrifices any attempt to refactor and know which parameters are allowed just by looking at thye method signature. Thus makes the use of proper code comments a critical issue (we all comment our code right?).

Fixed parameters

The name of this section is just a play of words (unfortunately section 2 did not follow the * parameters as it did in the Spanish version) because what I realy want to say is known "currying". Currying is a technique that transforms a function (in our case a closure) into another one while fixing one or more values, meaning that the fixed values remain constant for each invocation of the second function. Think "constant parameters" for the second function.
Listing 4.1

 

def getSlope = { x, y, b = 0 ->
println "x: y: b:"
(y - b) / x
}
assert 1 == getSlope( 2, 2 )
def getSlopeX = getSlope.curry(5)
assert 1 == getSlopeX(5)
assert 0 == getSlopeX(2.5,2.5)
// imprime
// x:2 y:2 b:0
// x:5 y:5 b:0
// x:5 y:2.5 b:2.5

 

First we define a closure with 3 parameters (with dynamic types) and what's more, the parameter b has a default value of 0, meaning that you may call the closure with just 2 parameters (another feature found in other languages like PHP). After asserting that the implementation of getSlope is accurate we proceed to curry it with x = 5, meaning that all calls to getSlopeX will have x = 5. So if we call getSlopeX(1) y will have 1 as value and b would have 0 as value (remember the default we assigned). And if you call getSlopeX with two parameters, then the second will be b's value.

Revisiting operators

In the second part of this series, we saw how Groovy enables operator overloading. This time we will take a look at other operators you may not see in Java. The first one is known as spaceship operator because if looks like a little UFO (<=>), it is of binary type (accepts two arguments) and executes a comparison, makeing it ideal for sorting algortithms. To enable its use with a type in particular, that class must implement java.util.Comparable, which has the side effect of enabling the following operators too: <, <=, =>, >.
Listing 5.1

 

// Number is Comparable
assert [1,2,3,4,5,6] == [2,4,6,1,5,3].sort {a,b -> a <=> b }

 

The second operator is named "spread" and has two variants. The first one allows the contents of a list to be expanded (like a reverse varargs), the second one applyes a method call to all elements in the list (used by GPath, Groovy's version of XPath but for beans, cool!). Let's see them in action:
Listing 5.2

 

// 1ra variant
def calculate = { a, b, c -> "$a,$b,$c" }
assert "1,2,3" == calculate( *[1,2,3] )
// 2da variant
assert ["01","11","21"] == ["10","11","12"]*.reverse()

 

The third operator in our list is Elvis (?:) which works like a simplified version of the ternary operator, as it will return the conditional expression if it evaluates to true. If it's, false then it will return the other expression
Listing 5.3

 

assert "hello" == "hello" ?: "bye"
assert "bye" == null ?: "bye"

 

The next operator should bring memories to those that used to work with the C language, as it allows you to get a reference to a method (in C it was a function pointer). In Groovy 1.1 its also possible to obtain a property reference. This feature is very useful to transform a method into a closure, and enables behavior mixin in a similar way as a Ruby does as you may call that closure from context of an external class (not in the hierarchy of the one that owns the method). Let's see in action with a revised version of Math and let's throw in curry just for fun
Listing 5.4

 

class Math {
static getSlope( x, y, b = 0 ){
(y - b) / x
}
}
// let's grab a reference to the static method
def getSlope = Math.&getSlope
assert getSlope instanceof Closure
assert 1 == getSlope( 2, 2 )
def getSlopeX = getSlope.curry(5)
assert 1 == getSlopeX(5)
assert 0 == getSlopeX(2.5,2.5)

 

The last operator is not exactly an operator per se, it is a reserved keyword (as) which enables Groovy casting, a better way to convert a type into another. It may be overridden/extended like an operator if your class has a method named asType( Class ). When used along with Closures and maps you may coerce that closure or map into a concrete implementation of an interface (abstract and concrete class coercion supported since Groovy 1.1)
Listing 5.5

 

import java.awt.event.*
import javax.swing.JButton
class Person {
String name
int id
}
def person = [name: 'Duke', id:1] as Person
assert person.class == Person
assert person.name == 'Duke'
def button = new JButton(label:'OK')
button.addActionListener({ event ->
assert 'OK' == event.source.label
} as ActionListener )
button.doClick()

 

Conclusion

That's all for the introductory material, I hope the features presented in this three part series have interested you in trying Groovy. In later articles I'd like to talk about Builders and the MOP (Meta Object Protocol), two advanced features that Groovy has to make life easier this side of Java.

Published at DZone with permission of its author, Andres Almiray.