A Taste of Groovy

Abstract

JVM popularised the idea of platform independence. However Java was the only language available to the developers who wanted to take advantage of the benefits of JVM. Developer productivity on Java was a real challenge and this lead to the development of Groovy language. Groovy is a dynamic language targeted to run on JVM and provides productivity benefits to the developers by embracing simplicity and being dynamic. Groovy is also highly interoperable with Java. In this article, I will walk you through code examples that demonstrate how Groovy is the language you always wanted Java to be.

Installation

  1. Download the distribution zip file from http://groovy-lang.org/download.html
  2. Extract the zip file
  3. Append the bin directory to PATH environment variable.

Note: If you are on a *nix system, I would recommend installing groovy though GVM (http://gvmtool.net/) (now SDKMAN), which would give you the ability to switch between versions. This is handy if you are working on multiple projects that are running on different versions of Groovy. Visit http://groovy-lang.org/download.html#gvm for instructions.

Hello World

Continuing the tradition, let's take a look at hello world program in Groovy stored in file 'Hello.groovy'.

println 'Hello Developers!'

To run the program, switch to the directory where 'Hello.groovy' is present and execute the command groovy Hello.groovy. Pause for a moment to imagine the amount of Java code required to achieve the same result!

The groovy command will compile Groovy code into JVM byte code and run it immediately, without storing the generated byte code in a .class file. You could use groovyc command to compile Groovy code into class files. In this example, Hello.groovy would compile to Hello.class. Now you could run this using java command, with the only addition of groovy-all jar being available in the class path.

[code ]$ java -cp $GROOVY_HOME/embeddable/groovy-all-2.3.7.jar:. Hello
Hello Developers!

When all you want is to run a sequence of instructions, Groovy doesn't force you to enclose them in a class and method explicitly. This makes Groovy a good candidate for scripting. If your script has to run on both Windows and *nix systems, you would still need only one script.

Groovy Beans

Let's take a look at a Groovy bean class.

class Person{
	String firstName
	String lastName
	int age
}

Person me = new Person()
me.firstName = 'Naresha'
println me.firstName

If you are a Java developer, I am sure you would get angry at me for accessing the attributes directly, without going through getter/setter methods. Let me illustrate that I haven't violated the rule.

To get the list of methods available in compiled Person class, let me add the following line of code, before I create the Person object.

println Person.metaClass.methods*.name

Output:

[equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait, __$swapInit, getAge, getFirstName, getLastName, getMetaClass, getProperty, invokeMethod, setAge, setFirstName, setLastName, setMetaClass, setProperty]
Naresha

You could spot getters and setters for firstName, lastName and age!

Let's further modify our Person class by adding customized getter and setter for firstName.

class Person{
	String firstName
	String lastName
	int age
	
	public String getFirstName(){
		println "Getter called"
		return firstName
	}
	
	public void setFirstName(String value){
		println "Setter called"
		firstName = value
	}
}

Person me = new Person()
me.firstName = 'Naresha'
println me.firstName

Output:
Setter called
Getter called
Naresha

That proves we are indeed invoking setter/getter, without accessing the fields directly. Groovy generates getters and setters automatically, helping to reduce noise in our code. Also you get syntactic sugar for accessing getter/setters, without having to invoke the methods explicitly.

Dynamic Groovy

Groovy is a dynamically typed language. You could use def keyword instead of the actual types.

def number = 10
println number.class // class java.lang.Integer
number = '10'
println number.class // class java.lang.String

def sayHello(){
	'hello'
}
def message = sayHello()
println message // hello

You could note that while declaring the variable number, I haven't specified a type. But during runtime, the variable number is of type java.lang.Integer. Now if you assign a String object to number, it would start exhibiting the behaviours of String. Thus Groovy is strongly typed and dynamically typed. Similarly you could use def keyword instead of a return type while declaring functions. You could also note that return keyword is optional. The value of the last expression in the function will be returned to the caller.

In Groovy, you also get the ability to invoke methods dynamically in an elegant way.

String methodName = 'toUpperCase'
println 'groovy'."$methodName"() // GROOVY

def me = new Person(firstName: 'Naresha')
String property = 'firstName'
println me."$property" // NARESHA

The methodName can be assigned during runtime, which is the real usecase of this feature.

Null Safe Dereferencing

def me = new Person()
println me.firstName.toUpperCase()

As you expect, the above code would throw a NullPointerException. A Java programmer would perform a not null check on firstName before trying to convert the firstName to uppercase. Groovy has a null safe dereference operator (?.). Let's see it in action.

def me = new Person()
println me.firstName?.toUpperCase() // null
me.firstName = 'Naresha'
println me.firstName?.toUpperCase() // NARESHA

Closures

In groovy, a closure is a block of code. One could assign a closure to a variable so that you could invoke them later. A closure can take arguments, return values and refer to variables in the lexical scope.

def add = { a, b ->
	a + b
}

println add(10, 20) // 30

You could notice the syntactic sugar provided by Groovy to invoke a closure in the same way you would invoke a function. You could pass a closure as an argument to functions as illustrated in the following example.

def perform(a, b, operation){
	operation(a,b)
}

println perform(10, 20, add) // 30

Native Support for List, Map and Range

Groovy provides elegant syntax for creating and working with List and Map objects.

def numbers = [1, 2, 3, 4]
println numbers.class //class java.util.ArrayList
// Add an element
numbers << 5
println numbers // [1, 2, 3, 4, 5]
//Get item at index 0
println numbers[0] // 1

Groovy uses the Java Collections library with some syntactic sugar.

def airports = [BLR: 'Bengaluru', DEL: 'New Delhi']
println airports.getClass().getName() // java.util.LinkedHashMap
//Add an entry
airports.BOM = 'Mumbai'
println airports // [BLR:Bengaluru, DEL:New Delhi, BOM:Mumbai]
//Get value for a key
println airports.BLR // Bengaluru

Note: airports.class implies accessing the entry with key 'class`. Hence for maps use the long form.

The following example illustrates how you can create range objects.

def range = 1..4
println range // [1, 2, 3, 4]
println range.class // class groovy.lang.IntRange

def alphabets = 'a'..'z'
println alphabets
println 'x' in alphabets // true
println 'A' in alphabets // false

Internal Iterators

Groovy enriches Java Collections with some convenience methods, with internal iterators being the most common ones. The idiomatic approach in Groovy is to use the internal iterators, instead of for or while loops.

def numbers = [9, 7, 4 ,1]
numbers.each { number ->
	println number
}

numbers.eachWithIndex { number, index ->
	println "${index+1}) $number"
}

Note that each is a method, which accepts a closure as the argument. The equivalent code with named closure is as follows

def printNumber = { number ->
	println number
}
numbers.each printNumber

Meta Programming

Metaprogramming is the ability to modify the behaviour of the program during runtime. Take the following code example.

def language = 'groovy'
println language.class // class java.lang.String
language.sayHello()

Since language is of type String, the method sayHello is expected to be present in String class, for the code to work. Since String does not contain sayHello method, the above code will throw a groovy.lang.MissingMethodException. But in Groovy, you could add such methods as follows.

def language = 'groovy'
language.metaClass.sayHello = { ->
	println "Hello from Groovy"
}
language.sayHello() // Hello from Groovy
'Java'.sayHello()

You could now invoke sayHello on the variable language, but not on any other String objects. We can fix this as follows.

String.metaClass.sayHello = { ->
	println "Hello from Groovy"
}

Now you could invoke sayHello on any String object. If you want the message to customised based on the value you are invoking upon, you could achieve that as well.

String.metaClass.sayHello = { ->
	println "Hello from $delegate"
}

A Real World Usecase

I was administering our team's wiki. The access was controlled by two configuration entries, one for read access and another for write access. I had to provide comma separated email ids against them. In our case, both entries were identical. The corporate email system was using the format of "Name@MyCompany.com", while the wiki expected it to be in "name@mycompany.com" format to work. I was getting individual ids through email or a list of them in an excel sheet, whenever new members joined the team.

To create the configuration entries, I had to convert the email ids to lowercase and separate them by comma.

I decided to maintain the active team members ids in a file input.txt in the first format (one id per line - handy when I copy from excel sheet). Here is a groovy script that would convert the contents of input.txt to comma separated email ids in the latter format.

import java.nio.file.Paths

def inputFile = Paths.get('./input.txt')
def output =  inputFile.readLines().collect{
	it.toLowerCase()
}.sort().join(', ')
Paths.get('./output.txt').text = output
println 'The result has been written to output.txt'

Whenever the team changed, all I had to do was update my input.txt, run the script and paste the contents of output.txt into read and write access configurations.

If you are using Groovy versions prior to 2.3, you have to use java.io.File , instead of java.nio.file.Path and Paths

Conclusion

Using Groovy can result in much smaller codebase at higher level of abstraction. Groovy has a number of features to empower developers with productivity benefits and is highly interoperable with Java.

[Originally published in December, 2014]

Show Comments