May 29, 2016

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


Pages: 1 2 3

Util classes

Groovy: (none)

Lombok:
@UtilityClass
public class UtilLombok {
    public String doSomething() {
        return "done!";
    }
}

UtilLombok.doSomething();
Lombok’s @UtilityClass annotation transforms a class into a typical *Util class: final, private constructor, static methods.

There is no Groovy equivalent.

Extension methods

Groovy:
class ExtensionsGroovy {
    public static String greetGroovy(String self) {
        return "Hello Groovy, this is " + self
    }
}

// In file src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule:
moduleName = lombokvsgroovy-module
moduleVersion = 1.0
extensionClasses = ch.codebulb.lombokvsgroovy.extensions.ExtensionsGroovy

// Called from Groovy code:
"World".greetGroovy() // "Hello Groovy, this is World"
Lombok:
public class ExtensionsLombok {
    public static String greetLombok(String self) {
        return "Hello Lombok, this is " + self;
    }
}

// Called from Java code:
@ExtensionMethod(ExtensionsLombok.class)
public class LombokVsGroovyTest {
 public void testExtensions() {
  "World".greetLombok() // "Hello Lombok, this is World"
 }
}
As its name suggests, Lombok’s @ExtensionMethod brings extension methods, as known e.g. from C#, to Java.

You must use @ExtensionMethod on the caller class to register your extensions.

Apart from other, more dynamic ways to extend Java classes at runtime, Groovy also provides so-called extension modules, although they come with a few caveats:
  • Even though they are compatible with @CompileStatic Groovy, hence working with the Groovy compiler, they don’t work with the Java compiler, i.e. you cannot call them from Java. It’s really the same as with e.g. the additional JDK methods which Groovy introduces: Just because you have Groovy on your class path doesn’t mean that you can call those “GDK” methods from your Java code.
  • Their sources must have been compiled in an earlier compiler run than the @CompileStatic sources which will use them. This is implicitly true for unit tests which got compiled separately from the main code; otherwise, you must compile your extension modules separately explicitly, e.g. in a separate Maven project.
  • They need explicit registration through an ExtensionModule descriptor.
Although from a design point of view, extension methods clearly are to be preferred over a collection of static *Util classes, this design choice should be carefully considered in both Lombok and Groovy: You’re still basically relying on a JVM hack which has a huge design impact on your coding style.

This is really made for if you want to design your own DSL, but I wouldn’t recommend its use for a standard enterprise project.

@Delegate

Groovy:
class DelegateGroovy {
    @Delegate
    private List<Integer> values = []
}

DelegateGroovy groovy = new DelegateGroovy();
groovy.add(1);
groovy.add(2);
groovy.add(3);
groovy.size(); // 3
Lombok:
public class DelegateLombok {
    @Delegate
    private List<Integer> values = new ArrayList<>();
}

DelegateLombok lombok = new DelegateLombok();
lombok.add(1);
lombok.add(2);
lombok.add(3);
lombok.size(); // 3
@Delegate works the same in Lombok and Groovy: Auto-delegate method calls to a field.

Note that in Groovy, the class will also be modified to implement all the interfaces of the delegate.

Most importantly, note please that this feature is in Lombok marked as “highly experimental”, and may possibly get removed in future versions.

This is yet another feature very useful for creating DSLs, but potentially long-term harmful in other scenarios.

Other common features

There is one additional annotation that both Lombok and Groovy share; we’ll not discuss its details here.
  • @Synchronized: A variant of the synchronized keyword.

Other Lombok-exclusive features

There are some additional annotations provided by Lombok without a Groovy equivalent:
  • @FieldDefaults: Set default modificators for all fields in a class.
  • @__: Meta-annotation to generate annotations while transforming the AST. “Highly experimental”!
  • @Helper: Generate method-local methods.

Other Groovy-exclusive features

Groovy does actually provide much more AST transformations than Lombok. Some of them are however for very specific purposes. The most useful ones are:
  • @InheritConstructors: Auto-inherit constructors (e.g. useful for Exception classes)
  • @AutoClone: Implement Cloneable / generate copy-constructor
  • @Sortable: Implement Comparable
These are generally very useful to implement common design patterns; it’s a pity they don’t exist in Lombok (yet).

Then of course, there’s much more to the Groovy language than just some silly AST transformations. There’s a huge amount of syntactic simplification Groovy brings, thus allowing you to write code far more concisely than Java.

Conclusion

This article really boiled down to a comparison of Lombok vs. Groovy AST transformations. Just as I have done with other topics, I created a simplified comparison table for your reference:

Feature Lombok Groovy Feature Lombok Groovy
def   X @Builder X X
@NonNull X   Unchecked exceptions X X
Getter / Setter X X @Log X  
@ToString X X @UtilityClass X  
@EqualsAndHashCode X X Extension methods X X
@TupleConstructor X X @Delegate X X
@Canonical X X @InheritConstructors   X
@Immutable X X @AutoClone   X
Immutable Setter X   @Sortable   X

Comparing those two libraries is really quite interesting. Although they have a completely different technical background, they share quite a lot of functionality and it’s nice to see that oftentimes, even the annotation names do match. Still, there are a few exclusive features to each library you’d like to see making its way into the other one.

Of course, no one would make a strict Lombok vs. Groovy assessment for a project. Rather, you would first of all ask yourself whether you can start your project on Groovy, and if not, whether you can include Lombok in your Java project. I can now honestly recommend to give it a shot.

However, this comparison also showed that it may actually make sense to combine Lombok and Groovy in a project, using Lombok’s exclusive features as a fallback for the use cases not (yet) covered by Groovy. Given the fact that combined compilation of both libraries is not an issue (as proven by the demo project), this may be a viable option to increase conciseness of a Groovy project even more.

I’ve implemented the most important comparisons covered in this article in JUnit tests which you can download from the demo project’s GitHub repository.

Please let me know whether you found this article interesting or if it lacks any information you were looking for. Feel free to share your Lombok vs. Groovy experience in the comments section as well.


Pages: 1 2 3

No comments:

Post a Comment