Just over a month ago the Groovy team released the latest version of Groovy, a dynamic programming language built specifically for the JVM (Java Virtual Machine). This version offers a variety of functionality and performance improvements over previous Groovy releases.
Note: The latest version of Groovy is actually 1.5.1 - a bug fix release. All code examples in this post were run using version 1.5.1. Click here to download the source code examples and follow along.
The Groovy Console
Groovy ships with a built in editor known as the Groovy console. The editor provides Groovy users a quick and easy way to test out their Groovy scripts. While it is certainly great to have a simple editor readily available, the Groovy 1.0 console really missed the mark in terms of user-friendliness.
The new Groovy console offers some basic, but greatly appreciated enhancements such as syntax highlighting, a simple shortcuts toolbar, and my personal favorite: support for keyboard shortcuts such as ctrl+f (find), ctrl+h (find and replace), and ctrl+z (undo).
In addition, a lot of the major IDEs are continuing to develop plug-in support for Groovy (and Grails):
Syntax Changes
A lot of new Java 5 features are now supported in the Groovy language including:
Generics
It might seem a bit strange to add such a statically typed feature into a dynamic language, but in order integrate seamlessly with Java versions greater than 5, Groovy now allows users to write generics into their Groovy code:
Listing 1. - generics/UseGroovyGenerics.java
public class UseGroovyGenerics {
public static void main(String[] args){
GroovyGenerics gg = new GroovyGenerics();
gg.getList().add(1);
gg.getList().add(2);
for(Integer i : gg.getList()){
System.out.println("List element: " + i);
}
}
}
Listing 2. - generics/GroovyGenerics.groovy
public class GroovyGenerics {
List<Integer> list = []
}
Listing 3. - Output of Running UseGroovyGenerics
List element: 1 List element: 2
Using generics in a purely Groovy environment does not really provide much of a benefit because Groovy itself does not actually enforce generics. Aside from causing confusion, users will run into real trouble when attempting to do something like this:
Listing 4. - generics/generics_class_cast_exception.groovy
List<Integer> list1 = [1, 2, 3, 4, 5]
assert list1 == [1, 2, 3, 4, 5]
list1.each {
Integer l = it //works fine
}
List<Integer> list2 = [1, 2, 3, 4, 5, 6, "seven"]
assert list2 == [1, 2, 3, 4, 5, 6, "seven"] //No problems yet
list2.each {
Integer l = it //ClassCastException
}
The previous code will throw a ClassCastException when list2 attempts to store a String in an Integer. The bottom line is that if needed, generics are now supported in Groovy.
Enums
Listing 5. - enums.groovy
enum Season {
spring, summer, fall, winter
}
def seasonActivity(Season season) {
activity = ''
switch (season) {
case Season.spring:
activity = 'soccer'
break
case Season.summer:
activity = 'baseball'
break
case Season.fall:
activity = 'football'
break
case Season.winter:
activity = 'basketball'
break
}
return activity
}
println "Let's play: ${seasonActivity(Season.spring)}"
println "Let's play: ${seasonActivity(Season.summer)}"
println "Let's play: ${seasonActivity(Season.fall)}"
println "Let's play: ${seasonActivity(Season.winter)}"
Listing 6. - Output of enums.groovy
Let's play: soccer Let's play: baseball Let's play: football Let's play: basketball
Annotations
Groovy 1.5 also supports annotations, allowing users to store metadata information directly in the source code. Get rid of those gross XML files and give annotations a shot. For more information check out the Groovy Annotations page
The previous examples (generics, enums, and annotations) require JDK version 5 or greater; however, some other changes have been made to Groovy that run on JDK version 1.4 or greater:
The Elvis Operator - ?:
The Elvis operator is simply a shorthand version of Java's ternary operator. It's most useful when a default value is needed in place of an object that returns null or false.
Listing 7. - elvis_op.groovy
name = ""
printName = name ?: "friend"
println "Hello $printName"
name = "Justin"
printName = name ?: "friend"
println "Hello $printName"
name = null
println "Hello ${name ?: 'friend'}" //using a GString
Listing 8. - Output of elvis_op.groovy
Hello friend Hello Justin Hello friend
For more information about "Groovy Truth" please see the Groovy documentation
The Classical for Loop
Groovy 1.5 goes retro and adds Java's classical for loop into its syntax. This should make it even easier for Java programmers to pick up the Groovy language. As an added side effect, the classical for loop provides a counter variable that can be used inside of the loop's scope:
Listing 9. - classical_for_loop.groovy
/* classical for loop */
for (int i = 1; i <= 5; i++) {
println "$i Hello" // i can be used as a counter variable
}
helloList = ['Hello', 'Hello', 'Hello', 'Hello', 'Hello']
print "\n"
/* for / in loop */
int i = 1; // a seperate counter variable is needed
for (String hi : helloList) {
println "$i $hi"
i++;
}
Listing 10. - Output of classical_for_loop.groovy
1 Hello 2 Hello 3 Hello 4 Hello 5 Hello 1 Hello 2 Hello 3 Hello 4 Hello 5 Hello
Performance
The official Groovy web site claims that Groovy 1.5 "provides significant performance gains." In most cases, faster is better so this is a very welcome enhancement to the language. To put Groovy's performance to the test run the following script to read a file containing 100,000 numbers in random order, sort them, and then output them in ascending order to a new file. The code and results are below:
Listing 11. - Machine Specs
Intel Centrino Core 2 Duo @ 2.0GHz 1 GB of RAM Windows XP/SP2 Java 1.6.0_04
To generate the file with the 100,000 numbers in random use this simple Groovy script:
Listing 12. - performance/random_number_file_generator.groovy
def randomNumberFile = new File('random_numbers.txt')
def lines = (1..100000).toList()
Collections.shuffle(lines)
randomNumberFile.write(lines[0..99999].join("\r\n"))
Please note that the same random number file was used to test both Groovy 1.0 and 1.5.
Once the file is created, the performance of both Groovy 1.0 and 1.5 can be tested using the following script:
Listing 13. - performance/performance.groovy
start = new Date()
def randomNumberFile = new File('random_numbers.txt')
List sortedList = []
randomNumberFile.eachLine {
sortedList << new Integer(it) // cast for proper sorting
}
def sortedNumberFile = new File('sorted_numbers.txt')
sortedNumberFile.write(sortedList.sort()[0..99999].join("\r\n"))
totalTime = (new Date().time - start.time) / 1000
println "Total time $totalTime"
Listing 14. - Groovy 1.0 vs Groovy 1.5 Performance Results
The results:
Groovy 1.0 average speed: 4.281 seconds
Groovy 1.5 average speed: 2.828 seconds
Wow! That's a 33.9% reduction in processing time. Although this test is far from official, it does seem to indicate that Groovy 1.5 provides significant performance improvements over Groovy 1.0.
Conclusion
The performance improvements alone justify upgrading to Groovy 1.5, but if that's not enough, Groovy 1.5 also comes with a new joint compiler that compiles mixed Java and Groovy code together without having to setup a specific compilation order as was necessary in previous versions. Many major enhancements have been added to this new version of Groovy giving programmers more power and flexibility while decreasing the time it takes to do their work. Overall, Groovy 1.5 is a major and welcome improvement to the Groovy language.

