Mads“ coding thoughts

home

Using Groovy to make for better domain objects in a Java application

20 Mar 2014

Have you ever considered how we write Plain Old Java Objects? POJOs, if coded correctly they are just objects to contain state, along with methods to access, modify and compare instances to other instances. Nothing more. The methods are coded the same for every POJO. Since this is so common we have wizards and generators in the editors for a more enjoyable experience. The generators work well, however they create a lot noise in the code that needs to be maintained. Just make sure you generate that hashCode when you generate that equals, or the Java gods will get mad for breaking their contract. Every time you add a new member variable, you need to add new getter/setter, regenerate the hashCode and equals and sometimes even the toString. Afterwards you version control the changes, which may lead to merge conflicts or errors.

A domain class in Groovy is called a POGO (Plain Old Groovy Object). Instead of explaining what that actually mean here's a simple example:

//Employee.groovy
class Employee {
    int id, age
    String firstName, lastName
}

The really interesting part is when we compile this to Java bytecode (compile command: groovyc Employee.groovy). Running the compiled class with javap (Java class file disassembler) we can examine what we actually got.

> javap Employee.class
Compiled from "Employee.groovy"
public class Employee implements groovy.lang.GroovyObject {
    ...
        public Employee();
    public java.lang.String getFirstName();
    public void setFirstName(java.lang.String);
        public java.lang.String getLastName();
    public void setLastName(java.lang.String);
    public int getAge();
    public void setAge(int);
    public int getId();
    public void setId(int);
    ...
}

Yes, this looks exactly (well almost) like a regular POJO. Except we got all these getters and setters for free. And you probably guessed it, we can use this compiled class just like regular a Java class inside Java source code.

If adding the annotation @Canonical before the class declaration, we get even more for free.

//Employee.groovy
@Canonical
class Employee {
    int id
    String firstName, lastName
    int age
}

@Canonical will generate constructors for all our class member variables, a correctly implemented hashCode, equals, toString and give us tuple constructors for every class member.

And if you don't believe me, here is the javap command run once more for the Employee.class.

public class Employee implements groovy.lang.GroovyObject {
...
  public Employee(java.lang.String, java.lang.String, int, int);
  public Employee(java.lang.String, java.lang.String, int);
  public Employee(java.lang.String, java.lang.String);
  public Employee(java.lang.String);
  public Employee();
  public int hashCode();
  public boolean canEqual(java.lang.Object);
  public boolean equals(java.lang.Object);
  public java.lang.String toString();
  public java.lang.String getFirstName();
  public void setFirstName(java.lang.String);
  public java.lang.String getLastName();
  public void setLastName(java.lang.String);
  public int getAge();
  public void setAge(int);
  public int getId();
  public void setId(int);
...
}

What exactly is the Groovy compiler doing with this magical @Canonical one might wonder. Well this is what they call an AST transformation. I did not mention that earlier on purpose because it sounds scarier than it is. Groovy has more AST transformations which you can look at in the Groovy library, but @Canonical is probably one of the most powerful ones in Groovy.

Since Groovy compiles POGOs into 100% Java bytecode compatible classes, we can take advantage of Groovys features to make more readable and maintainable domain classes in Java applications. There are many other interesting features in Groovy that we can take advantage of to make for cleaner Java applications, but I think this is a very good and basic start. Now the slightly tricky part (assuming you are using a build tool). For this to work correctly the Groovy class has to be compiled before the Java source code where it is referenced. Which will be the topic for the next post.