http://www.developer.com/

Back to article

Groovy Code Notation: Like Java, Only Different


May 21, 2010

Like many languages, Groovy has a language specification that breaks down code into statements, expressions, and so on. Learning a language from such a specification tends to be a dry experience and doesn't take you far towards the goal of writing useful Groovy code in the shortest possible amount of time. Instead, we will present simple examples of typical Groovy constructs that make up most Groovy code: classes, scripts, beans, strings, regular expressions, numbers, lists, maps, ranges, closures, loops, and conditionals.

Take this article as a broad but shallow overview. It won't answer all your questions, but it will allow you to start experimenting with Groovy on your own. We encourage you to play with the language -- if you wonder what would happen if you were to tweak the code in a certain way, try it! You learn best by experience.

This article is taken from the book "Groovy in Action, Second Edition" (Manning Publications; ISBN: 9781935182443), written by Dierk König, Paul King, Guillaume Laforge, and Jon Skeet.

Declaring classes

Classes are the cornerstone of object-oriented programming because they define the blueprints from which objects are created.

Listing 1 contains a simple Groovy class named Book, which has an instance variable title, a constructor that sets the title, and a getter method for the title. Note that everything looks much like Java, except there's no accessibility modifier: methods are public by default.

Listing 1. A simple Book class 
class Book {
private String title
Book (String theTitle) {
    title = theTitle
}
String getTitle(){
    return title
}
}


The code is not surprising. Class declarations look much the same in most object-oriented languages.

Using scripts

Scripts are text files, typically with an extension of .groovy that can be executed from the command shell via

> groovy myfile.groovy


Note that this is very different from Java. In Groovy, we are executing the source code! An ordinary Java class is generated for us and executed behind the scenes. But from a user's perspective, it looks like we are executing plain Groovy source code. (Any Groovy code can be executed this way as long as it can be run; that is, it is either a script, a class with a main method, a Runnable, or a GroovyTestCase.)

Scripts contain Groovy statements without an enclosing class declaration. Scripts can even contain method definitions outside of class definitions to structure the code better.

Listing 2 shows how easy it is to use the Book class in a script. We create a new instance and call the getter method on the object by using Java's dot-syntax. Then we define a method to read the title backwards.

Listing 2 Using the Book class from a script 
Book   gina = new Book('Groovy in Action')

assert gina.getTitle()         == 'Groovy in Action'
assert getTitleBackwards(gina) == 'noitcA ni yvoorG'

String getTitleBackwards(book) {
    String title = book.getTitle()
 return title.reverse()
}


Note how we are able to invoke the method getTitleBackwards before it is declared. Behind this observation is a fundamental difference between Groovy and scripting languages such as Ruby. A Groovy script is fully constructed -- that is, parsed, compiled, and generated -- before execution.

Another important observation is that we can use Book objects without explicitly compiling the Book class! The only prerequisite for using the Book class is that Book.groovy must reside on the classpath. The Groovy runtime system will find the file, compile it transparently into a class, and yield a new Book object. Groovy combines the ease of scripting with the merits of object orientation.

This inevitably leads to the question of how to organize larger script-based applications. In Groovy, the preferred way is not to mesh numerous script files together but, instead, to group reusable components into classes such as Book. Remember that such a class remains fully scriptable; you can modify Groovy code and the changes are instantly available without further action.

It was pretty simple to write the Book class and the script that used it. Indeed, it's hard to believe that it can be any simpler -- but it can, as we'll see next.

GroovyBeans

JavaBeans are ordinary Java classes that expose properties. What is a property? That's not easy to explain because it is not a single standalone concept. It's made up from a naming convention. If a class exposes methods with the naming scheme getName() and setName(name), then the concept describes name as a property of that class. The get- and set- methods are called accessor methods. (Some people make a distinction between accessor and mutator methods, but we don't.) Boolean properties can use an is- prefix instead of get-, leading to method names such as isAdult.

A GroovyBean is a JavaBean defined in Groovy. In Groovy, working with beans is much easier than in Java. Groovy facilitates working with beans in three ways:

  • Generating the accessor methods
  • Allowing simplified access to all JavaBeans (including GroovyBeans)
  • Simplified registration of event handlers together with annotations that declare a property as bindable

Listing 3 shows how our Book class boils down to a one-liner defining the title property. This results in the generation of accessor methods getTitle() and setTitle(title).

Listing 3 Defining the BookBean class as a GroovyBean 
class BookBean {
 String title                                // #1 Property declaration
}

def groovyBook = new BookBean()

groovy-
     Book.setTitle('Groovy conquers the world')   // #2 Property use with explicit method calls
assert groovyBook.getTitle() == 'Groovy conquers the world' // #2

groovy-
     Book.title = 'Groovy in Action'           // #3 Property use with Groovy shortcuts
assert groovyBook.title == 'Groovy in Action'   // #3


We also demonstrate how to access the bean the standard way, with accessor methods, and the simplified way, where property access reads like direct field access.

Note that listing 3 is a fully valid script and can be executed as is, even though it contains a class declaration and additional code. Also note that groovyBook.title is not a field access. Instead, it is a shortcut for the corresponding accessor method. It would work even if we'd explicitly declared the property longhand with a getTitle method.

Annotations for AST transformations

In Groovy, you can define and use annotations just like in Java, which is a distinctive feature among JVM languages. Beyond that, Groovy also uses annotations to mark code structures for special compiler handling. Let's have a look at one of those annotations that comes with the Groovy distribution: @Immutable.

A Groovy bean can be marked as immutable, which means that the class becomes final and all its fields become final, and you cannot change its state after construction. Listing 4 declares an immutable FixedBean class, calls the constructor in two different ways, and asserts that we have a standard implementation of equals() that supports comparison by content. With the help of a little try-catch, we assert that changing the state is not allowed.

Listing 4 Defining the immutable FixedBean and exercising it
@Immutable class FixedBook {                         // #1 AST annotation
    String title
}

def gina   = new FixedBook('Groovy in Action')       // #2 positional ctor
def regina = new FixedBook(title:'Groovy in Action') // #3 named arg ctor

assert gina.title == 'Groovy in Action'
assert gina == regina                           // #4 standard equals()

try {
    gina.title = "Oops!"                        // #5 not allowed!
    assert false, "should not reach here"
} catch (ReadOnlyPropertyException e) {}


It must be said that proper immutablity is not easily achieved without such help and the AST transformation does actually much more than what we see above: it adds a correct hashCode() implementation and enforces defensive copying for access to all properties that aren't immutable by themselves.

Immutable types are always helpful for a clean design but they are indispensable for concurrent programming.

The @Immutable AST transformation is only one of the many that can enhance your code with additional characteristics. The full range comes with the GDK, including @Bindable, @Category, @Mixin, @Delegate, @Lazy, @Singleton, and @Grab.

The acronym AST stands for abstract syntax tree, which is a representation of the code that the Groovy parser creates and the Groovy compiler works upon to generate the bytecode. In between, AST transformations can modify that AST to sneak in new method implementations or add, delete, or modify any other code structure. This approach is also called compile-time meta-programming and is not limited to the transformations that come with the GDK. You can also provide your own transformations!

Handling text

Just like in Java, character data is mostly handled using the java.lang.String class. However, Groovy provides some tweaks to make that easier, with more options for string literals and some helpful operators.

GStrings

In Groovy, string literals can appear in single or double quotes. The double-quoted version allows the use of placeholders, which are automatically resolved as required. This is a GString, and that's also the name of the class involved. The following code demonstrates a simple variable expansion, although that's not all GStrings can do:

def nick = 'ReGina'
def book = 'Groovy in Action, 2nd ed.'
assert "$nick is $book" == 'ReGina is Groovy in Action, 2nd ed.'


As you'd expect, GStrings are pretty neat.

Regular expressions

If you are familiar with the concept of regular expressions, you will be glad to hear that Groovy supports them at the language level.

Groovy makes it easy to declare regular expression patterns, and provides operators for applying them. Figure 1 declares a pattern with the slashy // syntax and uses the =~ find operator to match the pattern against a given string. The first line ensures that the string contains a series of digits; the second line replaces every digit with an x.

Regular expression support in Groovy
Figure 1.
Regular expression support in Groovy through operators and slashy strings.

Note that replaceAll is defined on java.lang.String and takes two string arguments. It becomes apparent that '12345' is a java.lang.String, as is the expression /\d/.

Numbers are objects

Hardly any program can do without numbers, whether for calculations or (more frequently) for counting and indexing. Groovy numbers have a familiar appearance but, unlike in Java, they are first-class objects rather than primitive types.

In Java, you cannot invoke methods on primitive types. If x is of primitive type int, you cannot write x.toString(). On the other hand, if y is an object, you cannot use 2*y.

In Groovy, both are possible. You can use numbers with numeric operators, and you can also call methods on number instances.


def x = 1
def y = 2
assert x + y     == 3
assert x.plus(y) == 3
assert x instanceof Integer


The variables x and y are objects of type java.lang.Integer. Thus, we can use the plus method. But we can just as easily use the + operator.

This is a surprising and major lift to object orientation on the Java platform. Whereas Java has a small but ubiquitous part of the language that isn't object oriented at all, Groovy makes a point of using objects for everything.

Using lists, maps, and ranges

Many languages, including Java, only have direct support for a single collection type -- an array -- at the syntax level and have language features that only apply to that type. In practice, other collections are widely used, and there is no reason why the language should make it harder to use those collections than arrays. Groovy makes collection handling simple, with added support for operators, literals, and extra methods beyond those provided by the Java standard libraries.

Lists

Java supports indexing arrays with a square bracket syntax, which we will call the subscript operator. Groovy allows the same syntax to be used with lists -- instances of java.util.List -- which allows adding and removing elements, changing the size of the list at runtime, and storing items that are not necessarily of a uniform type. In addition, Groovy allows lists to be indexed outside their current bounds, which again can change the size of the list. Furthermore, lists can be specified as literals directly in your code.

The following example declares a list of Roman numerals and initializes it with the first seven numbers, as shown in figure 2.

An example list in Groovy
Figure 2.
An example list where the content for each index is the Roman numeral for that index.

The list is constructed such that each index matches its representation as a Roman numeral. Working with the list looks like we're working with an array, but in Groovy, the manipulation is more expressive, and the restrictions that apply to arrays are gone:

def roman = ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII'] // #1 List of Roman numerals
assert roman[4] == 'IV'     // #2 List access

roman[8] = 'VIII'           // #3 List expansion
assert roman.size() == 9


Note that there was no list item with index 8 when we assigned a value to it. We indexed the list outside the current bounds.

Simple maps

A map is a storage type that associates a key with a value. Maps store and retrieve values by key, whereas lists retrieve them by numeric index.

Unlike Java, Groovy supports maps at the language level, allowing them to be specified with literals and providing suitable operators to work with them. It does so with a clear and easy syntax. The syntax for maps looks like an array of key-value pairs, where a colon separates keys and values. That's all it takes.

The following example stores descriptions of HTTP return codes in a map (Hypertext Transfer Protocol, the protocol used for the World Wide Web. The server returns these codes with every response. Your browser typically shows the mapped descriptions for codes above 400.), as depicted in figure 3.

An example list in Groovy
Figure 3.
An example map where HTTP return codes map to their respective messages

You can see the map declaration and initialization, the retrieval of values, and the addition of a new entry. All of this is done with a single method call explicitly appearing in the source code -- and even that is only checking the new size of the map:

def http = [
        100 : 'CONTINUE',
        200 : 'OK',
        400 : 'BAD REQUEST'
]
assert http[200] == 'OK'
http[500] = 'INTERNAL SERVER ERROR'
assert http.size() == 4


Note how the syntax is consistent with that used to declare, access, and modify lists. The differences between using maps and lists are minimal, so it's easy to remember both. This is a good example of how Groovy language designers take commonly required operations and make the programmers' lives easier by providing a simple and consistent syntax.

Ranges

Although ranges don't appear in the standard Java libraries, most programmers have an intuitive idea of what a range is -- effectively a start point and an end point, with an operation to move between the two in discrete steps. Again, Groovy provides literals to support this useful concept, along with other language features such as the for statement, which understands ranges.

The following code demonstrates the range literal format, along with how to find the size of a range, determine whether it contains a particular value, find its start and end points, and reverse it:

def x = 1..10
assert x.contains(5)
assert x.contains(15) == false
assert x.size() == 10
assert x.from == 1
assert x.to == 10
assert x.reverse() == 10..1


These examples are limited because we are only trying to show what ranges do on their own. Ranges are usually used in conjunction with other Groovy features.

So much for the usual datatypes. We will now come to closures, a concept that doesn't exist in Java, but which Groovy uses extensively.

Code as objects: closures

The concept of closures is not a new one, but it has usually been associated with functional languages, allowing one piece of code to execute an arbitrary piece of code that has been specified elsewhere.

In object-oriented languages, the method-object pattern has often been used to simulate the same kind of behavior by defining types whose sole purpose is to implement an appropriate single-method interface so that instances of those types can be passed as arguments to methods, which then invokes the method on the interface.

A good example is the java.io.File.list(FilenameFilter) method. The FilenameFilter interface specifies a single method, and its only purpose is to allow the list of files returned from the list method to be filtered while it's being generated.

Unfortunately, this approach leads to an unnecessary proliferation of types, and the code involved is often widely separated from the logical point of use. Java uses anonymous inner classes to address these issues, but the syntax is clunky and there are significant limitations in terms of access to local variables from the calling method. Groovy allows closures to be specified inline in a concise, clean, and powerful way, effectively promoting the method-object pattern to a first-class position in the language.

Because closures are a new concept to most Java programmers, it may take a little time to adjust. The good news is that the initial steps of using closures are so easy that you hardly notice what is so new about them. The aha-wow-cool effect comes later, when you discover their real power.

Informally, a closure can be recognized as a list of statements within curly braces, like any other code block. It optionally has a list of identifiers in order to name the parameters passed to it, with an -> arrow marking the end of the list.

It's easiest to understand closures through examples. Figure 4 shows a simple closure that is passed to the List.each method, called on a list [1, 2, 3].

An example list in Groovy
Figure 4.
A simple example of a closure that prints the numbers 1, 2, and 3

The List.each method takes a single parameter -- a closure. It then executes that closure for each of the elements in the list, passing in that element as the argument to the closure. In this example, the main body of the closure is a statement to print out whatever is passed to the closure, namely the parameter we've called entry.

Let's consider a slightly more complicated question: if n people are at a party and everyone clinks glasses with everybody else, how many clinks do you hear? (Or, in computer terms: what is the maximum number of distinct connections in a dense network of n components?) Figure 5 sketches this question for five people, where each line represents one clink.

An example list in Groovy
Figure 5.
Five elements and their distinct connections, modeling five people (the circles) at a party clinking glasses with each other (the lines). Here we have 10 clinks.

To answer this question, we can use Integer's upto method, which does something for every Integer starting at the current value and going up to a given end value. We apply this method to the problem by imagining people arriving at the party one by one. As people arrive, they clink glasses with everyone who is already present. This way, everyone clinks glasses with everyone else exactly once.

Listing 5 shows the code required to calculate the number of clinks. We keep a running total of the number of clinks. When each guest arrives, we add the number of people already present (guestNumber–1). Finally, we test the result using Gauss's formula for this problem -- with 100 people, there should be 4,950 clinks. (Johann Carl Friedrich Gauss (1777–1855) was a German mathematician. At the age of seven, his teacher wanted to keep the kids busy by making them sum up the numbers from 1 to 100. Gauss discovered this formula and finished the task correctly and surprisingly quickly. There are different reports on how the teacher reacted.)

Listing 5 Counting all the clinks at a party using a closure
def totalClinks = 0
def partyPeople = 100
1.upto(partyPeople) { guestNumber ->
    clinksWithGuest = guestNumber-1
    totalClinks += clinksWithGuest     // #1 modifies outer scope
}
assert totalClinks == (partyPeople * (partyPeople-1)) / 2


How does this code relate to Java? In Java, we would have used a loop like the following snippet. The class declaration and main method are omitted for the sake of brevity:

// Java snippet
int totalClinks = 0;
int partyPeople = 100;
for(int guestNumber = 1;
        guestNumber <= partyPeople;
        guestNumber++) {
    int clinksWithGuest = guestNumber-1;
    totalClinks += clinksWithGuest;
}


Note that guestNumber appears four times in the Java code but only twice in the Groovy version. Don't dismiss this as a minor thing. The code should explain the programmer's intention with the simplest possible means and expressing behavior with two words rather than four is an important simplification.

Also note that the upto method encapsulates and hides the logic of how to walk over a sequence of integers. That is, this logic appears only one time in the code (in the implementation of upto). Count the equivalent for loops in any Java project and you'll see the amount of structural duplication inherent in Java.

The example has another subtle twist. The closure updates the totalClinks variable, which is defined in the outer scope. It can do so because it has access to the enclosing scope. That it pretty tricky to do in Java. (Java pours syntax vinegar over such a construct to discourage programmers from using it.)

Groovy control structures

Control structures allow a programming language to control the flow of execution through code. There are simple versions of everyday control structures like if-else, while, switch, and try-catch-finally in Groovy, just like in Java.

In conditionals, null is treated like false and so are empty strings, collections, and maps. The for loop has a for(i in x) { body } notation, where x can be anything that Groovy knows how to iterate through, such as an iterator, an enumeration, a collection, a range, a map -- or literally any object. In Groovy, the for loop is often replaced by iteration methods that take a closure argument. Listing 6 gives an overview.

Listing 6 Control structures
if (false) assert false     // #1 'if' as one-liner

if (null)                   // #2 Null is false
{                           // #3 Blocks may start on new line
    assert false
}
else
{
    assert true
}

def i = 0                   // #4 Classic 'while'
while (i < 10) {            // #4
    i++                     // #4
}                           // #4
assert i == 10              // #4

def clinks = 0                    // #5 'for' in 'range'
for (remainingGuests in 0..9) {   // #5
    clinks += remainingGuests     // #5
}                                 // #5
assert clinks == (10*9)/2         // #5

def list = [0, 1, 2, 3]           // #6 'for' in 'list'
for (j in list) {                 // #6
    assert j == list[j]           // #6
}                                 // #6

list.each() { item ->             // #7 'each' method with a closure
    assert item == list[item]     // #7
}                                 // #7

switch(3)  {                       // #8 Classic 'switch'
    case 1 : assert false; break   // #8
    case 3 : assert true;  break   // #8
    default: assert false          // #8
}                                  // #8


The code in listing 6 should be self-explanatory. Groovy control structures are reasonably close to Java's syntax.

That's it for the initial syntax presentation. You've got your feet wet with Groovy and you should have the impression that it is a nice mix of Java-friendly syntax elements with some new interesting twists.

Summary

You've got the first impression of Groovy's code notation and found it both similar to and distinct from Java at the same time. Groovy is similar with respect to defining classes, objects, and methods. It uses keywords, braces, brackets, and parentheses in a very similar fashion; however, Groovy's notation is more lightweight. It needs less scaffolding code, fewer declarations, and fewer lines of code to make the compiler happy. This may mean that you need to change the pace at which you read code: Groovy code says more in fewer lines, so you typically have to read more slowly, at least to start with.

About the Author

Dierk König is a senior software developer, mentor and coach. He publishes in leading German magazines on software development and speaks at international conferences. He works at Canoo Engineering AG, Basel, Switzerland, where he is fellow and founding partner. Since the first edition of this book came out, he has worked almost exclusively with Groovy, be it as an engineer, architect, consultant, or trainer. His single and multi-day trainings on Groovy and Grails attract lots of attention. He joined the Groovy project in 2004, working as a committer ever since.

Guillaume Laforge is the official Groovy Project Manager and Head of Groovy Development at SpringSource. He founded G2One, Inc. (later acquired by SpringSource), a professional services company dedicated to sustaining and leading the development of both Groovy and Grails. You can meet Guillaume at conferences around the world where he evangelizes the Groovy dynamic language, Domain-Specific Languages in Groovy, and the agile Grails web framework.

Dr. Paul King leads ASERT, an organization based in Brisbane, Australia which provides software development, training, and mentoring services to customers wanting to embrace new technologies, harness best practices and innovate. He has been contributing to open source projects for nearly 20 years and has been an active committer to Groovy since he joined the project in 2006. Paul speaks at international conferences, publishes in software magazines and journals. He has the pleasure of working with Groovy on a regular basis with many of his agile customers.

Jon Skeet is a Java developer working for Google in London. He is a C# author and community leader, spending far too much time on the Stack Overflow developer Q&A site. Jon's re-learning Groovy along the course of this book, standing in the readers' shoes and pulling up the rest of the authoring team if they steam ahead too quickly. His aim is to help to translate the experience and knowledge of the other authors into the most readable and informative text possible.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date