May 29, 2016

Project Lombok: Making Java more Groovy (part 1 of 3)



Project Lombok brings AST transformation annotations as we know and love them from Groovy to Java. Having established that it is safe for use even in “enterprise level” Java projects, I’m taking a closer look at Lombok’s capabilities in this article, comparing its features to their Groovy counterpart.

This article primarily addresses Groovy developers which are keen on applying the same level of conciseness to Java, and are interested in how to achieve that using project Lombok. I will here concentrate on the most useful and prominent Lombok and Groovy AST transformations.

In this article, Groovy code samples have a blue background color, Lombok / Java code samples have an orange one. In contrast to Lombok’s official documentation, I will also show example caller code which in my opinion best illustrates the actual code transformation.

Side note: Running Maven + Groovy + Lombok

I created a demo project showcasing the feature comparison of Lombok and Groovy as a Maven project which you can download from its GitHub repository. Based on this stackoverflow answer, I configured the Maven pom.xml such that it works well with Groovy + Lombok (in NetBeans).

def

Groovy:
class DefGroovy {
    public static def value = "Hello"
    
    public static <T> T getValue(T value) {
        def ret = value;
        return ret;
    }
}

DefGroovy.value.getClass() // String
Lombok:
public class DefLombok {
    public static <T> T getValue(T value) {
        val ret = value;
        return ret;
    }
}
Lombok’s val is a very basic version of Groovy’s def, with the following restrictions:
  • It’s only available for local variables
  • It just simply copies the type of the initialization variable. Groovy def, on the other hand, even supports changing its type at runtime.
Also, tool support for NetBeans isn’t there yet.

I dont’t see this as a very useful feature of Lombok for now.

@NonNull

Groovy: (none)

Lombok:
public class NonNullLombok {
    public static String checkNonNull(@NonNull String cannotBeNull, String canBeNull) {
        return cannotBeNull + canBeNull;
    }
}

NonNullLombok.checkNonNull(null, "World") // NullPointerException!
Lombok comes with the ability to automatically add null-checks with useful exception messages to a method for every parameter annotated with @NonNull.

Unfortunately, there is no equivalent in Groovy.

Getter / setter generation

Groovy:
class GetterSetterGroovy {
    public String name
    int age = 10
    
    protected void setName(String name) {
        this.name = name
    }
}

new GetterSetterGroovy().getAge(); // 10
new GetterSetterGroovy().setName("My name");
Lombok:
@Getter @Setter
public class GetterSetterLombok {
    @Setter(AccessLevel.PROTECTED)
    public String name;
    private int age = 10;
}

new GetterSetterLombok().getAge(); // 10
new GetterSetterLombok().setName("My name");
@Getter / @Setter are used to create property accessors which in Groovy is achieved implicitly by omitting a field’s access modificator.

Note that Lombok offers more control over getter / setter creation than Groovy. Also, you can set a getter to cache its return value by setting the respective annotation value; in Groovy, this comes close to using the @Memoized annotation on the getter.

Also, you can place @Getter / @Setter on the class to generate accessors for all fields by default, mimicking a Groovy-like behavior.

Given the central role of JavaBeans and their properties in typical enterprise projects, this is one of Lombok’s main features.

On the other hand, of course, Lombok can’t provide Groovy’s GPath property access syntactic sugar to access properties using the dot notation.

@TupleConstructor

Groovy:
@TupleConstructor
class CombinedGroovy {
    String name
    int age = 10
}

new CombinedGroovy();
new CombinedGroovy("My name", 20);
Lombok:
@NoArgsConstructor
@AllArgsConstructor
public class CombinedLombok {
    private String name;
    private int age = 10;
}

new CombinedLombok();
new CombinedLombok("My name", 20);
Whilst Groovy has the single @TupleConstructor annotation to create constructors from properties, Lombok has three, and they are based on fields:
  • @AllArgsConstructor: Closest to the Groovy equivalent. Creates a constructor by default from all the fields.
  • @RequiredArgsConstructor: Same as @AllArgs…, but by default only based on fields marked as final / @NonNull.
  • @NoArgsConstructor: Unlike Groovy, @AllArgs… overrides the default contructor unless explicitly set. Thus you can re-introduce it using this annotation.
Also note that while in Groovy, @TupleConstructor becomes a no-op if any custom constructors are present (can be overridden by setting force=true), Lombok will generate constructors regardless of whether any of them are already present or not.

This is a classic Groovy and Lombok “quick win”.

@ToString

Groovy:
@ToString(includePackage=false, includeNames=true)
class CombinedGroovy {
    String name
    int age = 10
}

new CombinedGroovy().toString() // CombinedGroovy(name:null, age:10)
Lombok:
@ToString(includeFieldNames=true)
public class CombinedLombok {
    private String name;
    private int age = 10;
}

new CombinedLombok().toString() // CombinedLombok(name=null, age=10)
In both Groovy and Lombok, @ToString generates the toString() method. In Groovy, information is taken from the class’s properties; in Lombok, it’s taken from the fields. Their output is nearly identical.

However, the Groovy equivalent offers more options. It’s the only one which supports printing the fully qualified class name (that’s even the default).

This is a very useful debugging tool in both libraries.

@EqualsAndHashCode

Groovy:
@EqualsAndHashCode(includes="name")
@TupleConstructor
class CombinedGroovy {
    String name
    int age = 10
}

new CombinedGroovy("My name", 20).equals(new CombinedGroovy("My name", 30));
Lombok:
@EqualsAndHashCode(of={"name"})
@NoArgsConstructor
@AllArgsConstructor
public class CombinedLombok {
    private String name;
    private int age = 10;
}

new CombinedLombok("My name", 20).equals(new CombinedLombok("My name", 30));
This one is similar to @ToString, and as its name suggests, this creates the equals() and hashCode() methods accordingly. Again, its name is the same for both libraries, with the Groovy version offering slightly more customization options. As with @ToString, Groovy works with properties while Lombok works with fields.

This again is an extremely useful tool to reduce boilerplate noise in production classes.

Pages: 1 2 3