Published in 2007.
In the last few months you have probably come across an article on a webzine or magazine that talks about dynamic languages, on how "Ruby on Rails" promises to boost your productivity doing webapp development opposed to the "standard" JEE way, or new features for JDK7 (like closures) or the fact that the JVM is now the host of more than 200+ languages besides Java. It turns out Groovy is one such language. But aside from receiving its fare share of publicity like other languages such as JRuby, Jython or Scala, it also has great integration with the Java language itself, as well as features found in those previously mentioned languages. Continue reading to find out more about Groovy and what it has to offer to your developer toolbox.
First steps: "Hello world"
Let's start with a sample of the language in action with the 'defacto' Hello World:
Listing 2.1
public class HelloWorld { public static void main( String[] args ) System.out.println("Hello World!"); } }
Done! hold on a second, listing 2.1 looks a lot like Java. In fact, it is Java! this sample shows something really important to the Groovy language and that is that any Java class/object is also a Groovy class/object. Let's leave this "trick" behind and move forward to the next sample, in which will use the following Groovy features:
- Groovy supports dynamic typing, like JavaScript and Perl do. So we can drop type information on the
main()
declaration--even the return type. - The default visibility of methods and fields is public. We can drop that too.
- Every Groovy object has at its disposure the
println
which can be seen as a shortcut forSystem.out.println
. - The semicolon at the end-of-line is optional too, it follows the same rules as other languages like JavaScript.
We change the previous listing with this rules in mind and we obtain:
Listing 2.2
class HelloWorld { static main( args ){ println "Hello World!" } }
But wait, there is more! We have said that Groovy is a dynamic language, but that doesn't constrain it to be an interpreted language per se. In fact every Groovy class will be compiled down to Java byte code (something that differentiates it from other JVM languages), and yet you can write and execute Groovy code in the form of scripts (they too are compiled to byte code in case you were wondering), yielding the smallest Groovy code for the Hello World sample:
Listing 2.3
println "Hello World!"
You may also define classes in scripts and use them right away:
Listing 2.4
class HelloWorld { def greet( name ){ "Hello ${name}!" } } def hm = new HelloWorld() println hm.greet("Groovy")
In this example we have shown 3 other features of the language:
- The return type of the method
greet
is not of a specific type, so we use the reserved keyworddef
which can be seen as thevar
keyword in JavaScript. - It is not required to write the
return
keyword. The value of the last executed sentence will be used as the return value automatically. - The string
"Hello !"
is not a simplejava.lang.String
. It is, in fact, one of the sexiest features: a GString (short for Groovy String). These type of strings allow variable interpolation and arbitrary expression executing much like in Perl.
From theory to practice
Enough of samples already, show me how to run the code! These are the steps you must follow to install Groovy and execute the examples:
- Go to http://groovy-lang.org/download.html
- Download the latest stable release, 1.1-beta-3 at the time of this writing. (2.4.7 in this reprint).
- If you are installing on Windows you may want to have a look at the NSIS-Installer, with translations in English, Spanish, German and French.
- If you opted for a manual install (or not on Windows), unzip/untar the package in a directory of your choice, like c:\groovy (on Windows) or /usr/local/groovy on Unix.
- Define and environment variable GROOVY_HOME that points to that directory.
- Make sure that JAVA_HOME is also defined in your environment and points to a JRE 1.4+
- Make sure that both GROOVY_HOME/bin and JAVA_HOME/bin are available in your PATH
That's it! Now you are ready to start running Groovy scripts and classes, there are 3 ways to achieve that.
- The easiest one is to execute the Groovy interpreter (Hadn't we said before that Groovy was not an interpreted language?), well you still require a byte code interpreter much as Java requires one. Assuming there is a script file 'hello.groovy' with the contents of listing 2.3, you may execute it with the following command
groovy hello.groovy
- Use
groovysh
, which is an interactive shell where you can write Groovy code and execute it right away. - Use the Groovy console, this is the option I recommend for people starting with the language. It is a graphical tool that wil let you save and load scripts, write code and execute it on a separate region so that it won't be mixed.
Second round: data types
Now that you know how to execute Groovy code it will be easier for you to follow the examples and explore them at your leisure. Let's continue with the supported data types. Contrary to what Java offers, all types in Groovy are objects. There are no primitive data types, and what's more, the following listing shows one of the many ways to execute a loop through a series of integers:
Listing 4.1
3.times { println "hello" }
After executing this listing you will see the word 'hello' printed 3 times, which is exactly what the code says if you read it like an English sentence "3 times print 'hello'", right? If you were not shocked at the surprise of being able to execute a method call on a number, then you may be wondering why the code 'println "hello"' is not executed right away. The answer lies in that that block of code is known as a Closure, which we will review at a later time. For now it is important that you understand that closures are blocks of code.
So Groovy supports all Java data types (wrappers are used instead of primitives), it also treats lists and maps as basic types, like Perl and PHP do with 'arrays' and 'hashes'. Actually lists are instances of java.util.ArrayList
and maps of type java.util.HashMap
, so you will be able to call on them all Java methods related to those types. Groovy adds a little bit of syntactic sugar by allowing list indexes and map keys to be accessed with the '[]' operator, in the case of maps '.' is also allowed:
Listing 4.2
def list = [1,2,3,4,5] def map = ["key":"value"] assert list.get(0) == 1 assert list[0] == 1 assert map.get("key") == "value" assert map["key"] == "value" assert map.key == "value" assert map."key" == "value def emptyList = [] def emptyMap = [:] // lists grow dynamically // like JavaScript arrays do def list = [] list[9] = 10 assert list[9] == 10 assert list.size() == 10
Ranges are another useful type. They define a sequence of values that can be traversed forward or backward. They are usually used to create sublists but definitely have more uses than that.
// the operator [] used with strings // is like calling substring() or charAt() on the string println "Groovy"[0..3] // prints Groo (0..9).each { num -> print num } // prints 0123456789 ('a'..'f').each { letter -> print letter } // prints abcdef
Groovy adds extra methods to lists and maps. In fact it adds more methods to existing Java classes. The set of extra methods is known as the GDK. Many of them use closures, which is the next topic we will see.
Reusable code blocks
We can see some examples of closures in action inn listings 4.1 and 4.2. But what is a closure (properly)? You can think of them as reusable code blocks, that hold the scope information where they were defined:
Listing 5.1
def name = "Groovy" def greet = { println "Hello !" } greet() // prints Hello Groovy!
If you know JavaScript or Ruby then closures should be familiar to you. It's like defining anonymous functions in a script. Closures can have parameters too, both type and name. But because Groovy is a dynamic language you can omit the parameter type if not needed. Observe what happens if we skip the type definition:
Listing 5.2
def printout = { item -> print item } (0..9).each printout ('a'..'f').each printout // prints 0123456789abcdef
Closures are so ubiquitous that you will always have at least one parameter at your disposal even if you don't define a name for it. The default name is it
, thus allowing the previous code to be written as:
Listing 5.3
def printout = { print it } (0..9).each printout ('a'..'f').each printout // prints 0123456789abcdef // 5 ways to print 0123456789 [0,1,2,3,4,5,6,7,8,9].each printout (0..9).each printout 10.times printout 0.upto 9, printout for( num in [0,1,2,3,4,5,6,7,8,9]) printout.call(num)
You will notice that the for cycle must do an explicit method call, aptly named call
on the closure. This is because for can't take a closure as its body. It must be a block of code as in Java. Did you notice that parenthesis and curly braces are optional too?
Conclusion
What has been exposed so far is a little taste of what Groovy has to offer as a programming language. Nevertheless I hope it has been enough to wet your appetite and entice you to give it a try.