April 19, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Groovy Code Notation: Like Java, Only Different

  • May 21, 2010
  • By Dierk König, Guillaume Laforge, Paul King, Jon Skeet
  • Send Email »
  • More Articles »

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!


Tags: Groovy



Page 1 of 3



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel