Blog2j
 
Google search
Ricerca personalizzata
 
Introduction to Groovy for java developers
Groovy is a new programming language that can be considered a valid alternative to Java for many reasons:
  • its syntax is very similar to the java one but it adds some constructs inspired to python and ruby that simplify the use and make the code easier to read and maintain
  • it's a dynamic language but can be used also with typed objects
  • it's based on java jdk, all java classes are available in Groovy
  • a Groovy compilation produces .class files compatible with bytecode generated by a java compiler, so Java and Groovy classes can coexist (and interact with each other) in the same project
A Java developer will immediately be at ease with Groovy and will not have to start from scratch because he can use a familiar syntax, the same basic classes and even the same development environment.
Groovy was born from an idea by James Strachan that for the first time spoke in his blog in August 2003. The idea has then become a jsr and several versions have been released in a few years. Version 1.0 was released on January 2 2007, version 1.5 on December 9 2007.
In this tutorial we will see how to add some classes written in Groovy to an existing java project
We'll use Eclipse with groovy plugin (it can be installed as an Eclipse update from here, an howto is available if needed).
We start with a simple example: a form of import/export of an address book items. You can download the complete source code, it includes 3 package, one contains common interfaces, while their Java and Groovy implementations are into the remaing two. The common interfaces are:
  • two beans with the data
public interface Person {

    String getName();
    void setName(String n);
    String getSurname();
    void setSurname(String s);
    Date getBirthDate();
    void setBirthDate(Date d);
    Address getAddress();
    void setAddress(Address a);
}

public interface Address {

    String getStreet();
    void setStreet(String s);
    String getCity();
    void setCity(String c);
}

  • an interface to be implemented by data importers
public interface Importer<T> {
    List<T> executeImport();
}
  • an interface to be implemented by data exporters
public interface Exporter<T> {
    void executeExport(List<T> l);
}
We begin to add a bit of Groovy, you can add the needed library to the classpath by right clicking on the project and execute Groovy -> Add Groovy nature. Now we are ready to write our first Groovy class by selecting Groovy class option from the Eclipse new menu. First we create the data beans implementations:
class PersonGroovyImpl implements Person {
    String name
    String surname
    Date birthDate
    Address address
}

class AddressGroovyImpl implements Address {
    String street
    String city
}
At first glance it may seems something is missing... In fact everything is there! Let's compare the AddressGroovyImpl class to its java versione:
public class AddressJavaImpl implements Address {

    private String street;
    private String city;

    public String getStreet() {
        return street;
    }
    public void setStreet(String street) {
        this.street = street;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
}
4 lines of code against 18, let's see the reasons:
  • a class declaration does not need the public keyword, since Groovy classes are public by default
  • Groovy classes can contain properties, that are fields defined with no modifier (public, private or protected) but accessible by their own automatically added getters and setters
  • the line-ending semicolon is optional
Thanks to these features the task of writing beans is immediate and non-repetitive, bean classes are easy to read and any information (field names, types) is written in one place only.

Let us continue with the class that executes import from a text file, a java implementation is:

public class TxtImporterJavaImpl implements Importer<Person> {

    private final String fileName;

    public TxtImporterJavaImpl(String fileName) {
        this.fileName = fileName;
    }

    private static SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");

    public List<Person> executeImport() {
        List<Person> l = new ArrayList<Person>();
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(fileName));
            String line = reader.readLine();
            while (line != null) {
                String[] split = line.split("\\|");
                Person p = new PersonJavaImpl();
                p.setName(split[0]);
                p.setSurname(split[1]);
                p.setBirthDate(format.parse(split[2]));
                Address address = new AddressJavaImpl();
                address.setStreet(split[3]);
                address.setCity(split[4]);
                p.setAddress(address);
                l.add(p);

                line = reader.readLine();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return l;
    }
}
Exceptions are managed with runtime exceptions for simplicity, despite this error handling is not trivial. By analyzing the executeImport method we can note that a part of it deals with text file reading while another part performs the conversion from a file row to an object. So we can create a class that encapsulates the file reading code to be reused in other similar contexts
public class GenericTxtImporterJavaImpl<T> implements Importer<T> {

    private final String fileName;
    private final RowImporter<T> rowImporter;

    public GenericTxtImporterJavaImpl(String fileName, RowImporter<T> rowImporter) {
        this.fileName = fileName;
        this.rowImporter = rowImporter;
    }

    public List<T> executeImport() {
        List<T> l = new ArrayList<T>();
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(fileName));
            String line = reader.readLine();
            while (line != null) {
                l.add(rowImporter.executeImport(line));
                line = reader.readLine();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (reader != null) {
                try {
                &nbsp;   reader.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return l;
    }
}
an interface used by this class that convert a file row into an object:
public interface RowImporter<T> {
    T executeImport(String s);
}
and an implementation that create a Person object starting from a row:
public class PersonImporter implements RowImporter<Person> {

    private static SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");

    public Person executeImport(String s) {
        try {
            String[] split = s.split("\\|");
            Person p = new PersonJavaImpl();
            p.setName(split[0]);
            p.setSurname(split[1]);
            p.setBirthDate(format.parse(split[2]));
            Address address = new AddressJavaImpl();
            address.setStreet(split[3]);
            address.setCity(split[4]);
            p.setAddress(address);
            return p;
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
}
The interesting part of PersonImporter is the executeImport method. GenericTxtImporterJavaImpl needs to know which method must be invoked to transform a file row into an object. Thus RowImporter is used only for wrapping the method to pass. This situation is very frequent in java (for example in event listeners), and anonymous inner classes were introduced in order to write shorter implementations. For example we can use GenericTxtImporterJavaImpl without explicitly define an implementation of RowImporter:
List<Person> l = new GenericTxtImporterJavaImpl<Person>(
    "resources/people.txt",
    new RowImporter<Person>() {

        public Person executeImport(String s) {
                Person p = new PersonJavaImpl();
                //....
                return p;
        }
    }
).executeImport();
Using anonymous classes provides amore compac t but less readable code, especially for inexperienced developers.
The Groovy version of the Importer implementation is:
class TxtImporterGroovyImpl implements Importer<Person> {
   
    String fileName

    private static SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy")
   
    List<Person> executeImport() {
        List<Person> l = new ArrayList<Person>()
        new File(fileName).eachLine {
            String[] split = it.split("\\|")
            Person p = new PersonGroovyImpl()
            p.name = split[0]
            p.surname = split[1]
            p.birthDate = format.parse(split[2])
            Address address = new AddressGroovyImpl()
            address.street = split[3]
            address.city = split[4]
            p.address = address
            l.add(p)
        }
        return l
    }
}

In practice the class GenericTxtImporterJavaImp is already available in Groovy by the eachLine method of File, File is a standard java class to which were added utility methods used by Groovy code.

But what is that block of code after the eachLine method?

It's a closure, the native Groovy construct that allows passing a block as method argument. In practice a closure is an object containing a block of code, in this example yhe block is passed to the eachLine method (parentheses after the method are optional in case of closure). When a closure input parameter is just one it has not to be declared, the it variable is available within the closure scope. Method parameters can be specified in a list followed by the characters ->, for example:

def map = [ 'a', 7, 'b', new Date(), new Date() + 1 ].groupBy{ it.class }

map.eachWithIndex { e, index ->
    println "${index} Classe ${e.key} valori ${e.value}"
}
This code uses two closure (the second with an explicit parameter definition) and two methods of Map and List that accept a closure as input. Groovy defines many useful methods on collection that uses closures, for example each, collect, find, findAll, min, max. The groupBy method iterate on list items and brings together the elements based on the closure return value, in a method or a closure the keyword return may be omitted because it returned the evaluation of last command. The eachWithIndex method iterate on collection's items and passes each element and its iteration index to the closure. The instruction inside the closure uses GString, using the syntax ${variableName} you can easily create strings containing values of available variables.
Let's see how to implement a Groovy class that exports a list of people in a xml file. The corresponding java example should use dom or external libraries like jdom, jaxb or castor. This is the Groovy implementation:
class XmlExporterGroovyImpl implements Exporter {
   
    private String fileName
   
    XmlExporterGroovyImpl(String fileName) {
        this.fileName = fileName
    }
   
    private SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
   
    void executeExport(List<Person> people) {
        def writer = new FileWriter(fileName);
        MarkupBuilder builder = new MarkupBuilder(writer)
       
        try {
            builder.people(total: people.size()) {
                people.eachWithIndex { p, pos ->
                    builder.person(index: pos) {
                        name(p.name)
                        surname(p.surname)
                        birthDate(formatter.format(p.birthDate))
                        address {
                            street(p.address.street)
                            city(p.address.city)
                        }
                    }
                }
            }
        }
        finally {
            if (writer != null) {
                writer.close();
            }
        }
    }
}
This class uses Groovy builder, a concept a bit particular but very useful. In practice you can invoke on the builder object methods with name corresponding to the name of the xml element you want to create. These methods are not normally defined, calls are intercepted and xml code is created automatically according to the name of the method and to the method parameters. If a pair is passed to the method an attribute is added to the xml tree, if a single value is passed then it is inserted inside the xml element.
If you want to create nested elements just pass a closure that creates the inner elements. So it is much more difficult to explain the inner behaviour of a builder than to use it! Indeed there is a very close relationship between the code we have just seen and the generated xml, the indentation helps a lot because the Groovy code is indented in the same way of the generated xml code.
We conclude creating a simple Java class that reads a text file and exports the content in xml:
public class MainJava {

    public static void main(String[] args) {
        List<Person> people = new TxtImporterJavaImpl("resources/people.txt").executeImport();
        new XmlExporterGroovyImpl("resources/people.xml").executeExport(people);
    }
}
The main method calls methods of java classes and methods of Groovy classes, even the list of people imported by a java method is passed to a Groovy method that exports that list in xml. Groovy can also be used as a scripting language, so we can define code outside of class, for example:
package groovytutorial.groovyimpl

def dir = "resources"

def people = new TxtImporterGroovyImpl(fileName: "${dir}/people.txt").executeImport()
new XmlExporterGroovyImpl("${dir}/people.xml").executeExport(people)
In this code are also used not typed variables that can be defined with the keyword def (similar to var in JavaScript). On the Internet there are dissenting opinions about the use of variables not typed in Groovy, it depends on personal experience, probably a java developer will be much more at ease using typed variables.
Our example ended, it's time of some personal consideration:
  • Groovy is ideal if you need a scripting language to use in a java project to model dynamic behaviors
  • Groovy syntax is one of its most powerful feature, many things that are difficult or little intuitive in java can be easily written in Groovy
  • the perfect integration with existing Java code allows the gradual introduction of Groovy, a good start could be to write Groovy junit tests for a java application
  • Groovy ide support is still worst than the java one. For example the refactor menu is not present and the source menu is almost empty within the Eclipse IDE. In practice much of what makes the difference is missing

I leave the conclusions to you, my only concerns are due to the still incomplete Eclipse support but of course Groovy is a language that deserves to be tested.

More details about Groovy are available on official site and in the excellent book Groovy in action.
 
News