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

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


Pages: 1 2 3

@Canonical

Groovy:
@CompileStatic
@Canonical
class CanonicalGroovy {
    String name
    int age = 10
}

new CanonicalGroovy("My name", 20).equals(new CanonicalGroovy("My name", 20));
Lombok:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CanonicalLombok {
    private String name;
    private int age = 10;
}


new CanonicalLombok("My name", 20).equals(new CanonicalLombok("My name", 20)); // true
  • In Groovy, @Canonical combines @ToString, @EqualsAndHashCode and @TupleConstructor (with getter / setter generation enabled by plain Groovy already),
  • In Lombok, @Data combines @ToString, @EqualsAndHashCode, @Getter / @Setter for all fields, and @RequiredArgsConstructor.
In both cases, adding the individual annotations additionally overrides using their default values by @Canonical / @Data.

This really is just a catch-all solution to quickly build POJOs. However, at least in Groovy practice I’ve found that typically, you really want to use the individual annotations anyway for more specific code generation control. For Lombok, the use of @RequiredArgsConstructor is unfortunate because it forces you to still manually apply @AllArgsConstructor + @NoArgsConstructor if you want to replicate the Groovy behavior which seems more sensible.

Hence, unfortunately, this is almost useless in both libraries.

@Immutable

Groovy:
@Immutable
class ImmutableGroovy {
    String name
    int age = 10
}


new ImmutableGroovy();
new ImmutableGroovy("My name", 20).equals(new ImmutableGroovy("My name", 20)); // true
Lombok:
@Value
@AllArgsConstructor
public class ImmutableLombok {
    private String name;
    private int age = 10;

    public ImmutableLombok() {
        this.name = null;
    }
}

new ImmutableLombok();
new ImmutableLombok("My name").equals(new ImmutableLombok("My name"));
In Groovy, @Immutable applies @Canonical with generating getters only and creating “immutable” POJOs. In Lombok, @Value does the same with the @Data equivalent, plus all the fields become final, hence the output can derive from the Groovy version.

Immutable setters

Groovy: (none)

Lombok:
@NoArgsConstructor
@AllArgsConstructor
@Getter @Setter
@Wither
public class WitherLombok {
    private String name = "Default name";
    private int age;
}

WitherLombok lombok = new WitherLombok();
lombok.setAge(20);
WitherLombok lombok2 = lombok.withName("My name");
lombok2.getName(); // My name
lombok2.getAge(); // 20
Lombok’s @Wither generates and “immutable setter” for fields. Although this works similar to @Setter, the setter is invoked on a new object (a clone) previously constructed from calling the constructor with all the instances’ current field values. It’s hence best practice to use this in conjunction with @AllArgsConstructor.

Unfortunately, there is no Groovy equivalent.

@Builder

Groovy:
@Builder
class BuilderGroovy {
    String name
    int age
    List<String> siblings = []
}

BuilderGroovy groovy = BuilderGroovy.builder()
    .name("My name").age(20).siblings(Arrays.asList("First", "Second"))
    .build();
Lombok:
@Builder
public class BuilderLombok {
    String name;
    int age;
    @Singular
    List<String> siblings = new ArrayList<>();
}

BuilderLombok lombok = BuilderLombok.builder()
    .name("My name").age(20).sibling("First").sibling("Second")
    .build();
@Builder auto-generates the otherwise boilerplate-intense builder pattern both in Groovy and Lombok.

With the @Singular annotation, Lombok provides an enhancement to further facilitate dealing with collections. For reasons unknown, the respective feature request for Groovy has been rejected.

In case you’re searching for an equivalent to the Groovy Builder’s more concise SimpleStrategy, you’ll have to use Lombok’s combined @Accessors(fluent=true) @Getter @Setter:

Groovy:
@Builder(builderStrategy=SimpleStrategy, prefix="")
class SimpleBuilderGroovy {
    String name
    int age
    List<String> siblings = []
}

SimpleBuilderGroovy simpleGroovy = new SimpleBuilderGroovy()
    .name("My name").age(20);
Lombok:
@Accessors(fluent=true)
@Getter @Setter
public class SimpleBuilderLombok {
    String name;
    int age;
    List<String> siblings = new ArrayList<>();
}

SimpleBuilderLombok simpleLombok = new SimpleBuilderLombok()
    .name("My name").age(20);

Make exceptions unchecked

Groovy:
class ExceptionsGroovy {
    public static void tryToDoSomething() {
        doSomethingDangerous(); // no try-catch!
    }
    
    private static void doSomethingDangerous() { // no throws!
        throw new UselessException();
    }
    
    public static class UselessException extends Exception {
        
    }
}
Lombok:
public class ExceptionsLombok {
    public static void tryToDoSomething() {
        doSomethingDangerous(); // no try-catch!
    }
    
    @SneakyThrows
    private static void doSomethingDangerous() { // no throws!
        throw new UselessException();
    }
    
    public static class UselessException extends Exception {
        
    }
}
By annotating a method with @SneakyThrows, you replicate Groovy’s exception handling default behavior with Lombok, that is to make exception checking optional, even for checked exceptions.

Although in some situations, checked exceptions may be used effectively as a tool to make your business code more robust, some people will claim that the concept of checked exceptions in Java is all wrong per se (which becomes obvious when working with code which was designed without a clear idea about what checked exceptions are for), and advocate an optional-checked language design like in Groovy, Scala, or C#.

This really is a pinch by Lombok towards creating Java code which feels more “dynamically typed”. You should have a deep understanding about Java exception design, and use it sparingly even then. Note that in contrast to exceptions design with Groovy, this is a very local opt-out hook, and it can easily devolve into exception micro-management.

@Log

Groovy: (none)

Lombok:
@Log
public class LoggingLombok {
    public static void logError() {
        log.severe("An error occurred");
    }
}
In Lombok, @Log and its alternatives add a logger field which can be of any of the major Java logging frameworks classes.

Unfortunately, there is no Groovy equivalent.

Pages: 1 2 3

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

May 22, 2016

Delombok: Enterprise-safe Java AST transformations



Project Lombok brings abstract syntax tree (AST) transformations to Java, allowing you to generate boilerplate code at compile time through simple annotations. With the delombok Maven plugin, this becomes a safe choice even for enterprise projects.

This blog post describes how to easily apply Java code generation for typical boilerplate tasks using Project Lombok – and how to safely introduce it to a Java project of any scale, in a non-intrusive way.

You’ll find the source code of a nicely configured (NetBeans) project in the accompanying GitHub repository.

Why should you use it?

Project Lombok allows you to use simple Java annotations to modify or extend Java source code on compile time. This is a feature well-known from Groovy projects, which comes with numerous so-called AST transformations out-of-the-box, to e.g. generate equals() / hashCode() methods, constructors, or even implement advanced features such as method-delegation. With Lombok, these features are brought to vanilla Java.

This is not about saving time when coding. That’s what static code generators (e.g. in the IDE) are for. Actual coding is done once only. Afterwards, you’ll need to be able to read your code, and to keep it up to date. This is were static code generation fails as the generated boilerplate becomes part of the source code file, and it gets in the way every time you have to revise is. Statically generated code by its very nature is potentially out-of-date as soon as it was generated. This never happens with code generated at compile time.

Code generation with Lombok

In order to use Lombok, you need to declare its Maven dependency:
<dependencies> 
 <dependency> 
  <groupId>org.projectlombok</groupId> 
  <artifactId>lombok</artifactId> 
  <version>1.14.8</version>
 </dependency>
 ...
</dependencies>
Note: At the time of writing (May 2016), this does not work with Lombok’s most recent release (1.16.8): When used in NetBeans (8.1), the IDE itself recognizes the code generated by Lombok (e.g. in autocomplete), but compilation will fail nonetheless when trying to access generated code. According to this stackoverflow answer, the current solution is a downgrade to Lombok 1.14.8.
Then you can for instance have getters and setters generated at compile time:
public class GetterSetterExample {
    @Getter @Setter
    private int age = 10;
 ...
}
and use them statically typed:
GetterSetterExample example = new GetterSetterExample();
example.getAge();

Why wouldn’t you use it?

Lombok helps you keep your Java code concise, DRY, and overall clean.

There are still viable reasons why you may be still reluctant to apply it in your project: It introduces a dependency on a third party tool, and in this case, this comes with two drawbacks:
  • A new programming style is introduced which may not be endorsed by future code maintenance personal.
  • You become dependent on a tool which may break with future Java releases.
  • (Plus, its current version indeed seems to be broken at the moment, at least when used in NetBeans.)
The second point becomes a pressing concern when you read Lombok’s backstory which clearly states that it actually relies on internal VM APIs and as such may be considered a hack:

“There is no guarantee the private APIs won't change in a later JDK and break Project Lombok.”

It does so by lack of official support for annotation processors for Java:

“If what lombok does could be done without resorting to internal API, we'd have done something else, but it can't be done, so we resort to internal API usage.”
At least, it would be favorable to have a “revert to vanilla” option with Lombok. Luckily, there is one. Enter Delombok, which is an official part of Lombok.

Safe code generation with Delombok

In order to activate Delombok, you need to add its plugin to your pom:
<plugins>
 <plugin>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok-maven-plugin</artifactId>
  <version>1.16.8.0</version>
  <executions>
   <execution>
    <phase>generate-sources</phase>
    <goals>
     <goal>delombok</goal>
    </goals>
   </execution>
  </executions>
 </plugin>
 ...
</plugins>
Note that this method supports the most recent Lombok release (even in NetBeans):
<dependencies> 
 <dependency> 
  <groupId>org.projectlombok</groupId> 
  <artifactId>lombok</artifactId> 
  <version>1.16.8</version>
 </dependency>
 ...
</dependencies>
You should then create a separate source folder to you Java files on which you want to apply Lombok AST transformations:
<resources>
    <resource>
        <directory>src/main/lombok</directory>
        <includes>
            <include> **/*.java </include>
        </includes>
    </resource>
</resources>
That way, you get a project structure like this:
  • Project
    • src
      • java: Source code to which Delombok is not applied (optional)
      • lombok: Source code to which Delombok is applied
    • target
      • generated-sources
        • lombok: Source code generated by Delombok
You’ll need to create the src/lombok folder by hand. NetBeans IDE will then automatically detect this resources directory as an additional source folder and the <plugin>’s <phase>generate-sources</phase>/<goal>delombok</goal> sub-folder as an additional “Generated Sources” folder.
Then, apply Lombok AST transformations in your src/lombok code and use it statically typed, same as above.

Note that in this case, the modified source code is indeed placed into the target/generated-sources/lombok folder.

Because the src/lombok folder is not one of Maven’s source class folders, there will be no “duplicate class” errors, whilst you’re still perfectly able to reference “delomboked” classes from “untouched” ones, and vice versa.

Of course, this configuration is not recommended for the actual development process as the additional step of generating “delomboked” source code introduces additional complexity and, more importantly, a compile time penalty. Rather, take it as your safeguard in case you’ll ever need to revoke your decision to opt for Lombok in a project: Then, delombokify your code again, and go on with manual coding. What a pity!

Before coming to a decision, you may also want to check readability of the actual code generated during a delombok run: I found that at least for the most prominent AST transformations, namely getters / setters or contructor generation, it doesn’t look at all worse than what your average IDE generates. Also not that the output of lombok’s AST transformations are highly configurable.

Conclusion

I’m a huge advocate of the strategy to generate all non-trivial code artifacts. I do believe that creating less code is better, in accordance with DRY, as long as KISS is respected. That’s part of the reason why I also like Groovy very much, and its AST transformations in particular, and that’s as well why I wholeheartedly recommend Lombok for Java projects.

Since it’s both easy and safe to apply, there’s really hardly any reason to miss this opportunity for a clean code boost.

Again, you can download the NetBeans project skeleton from its GitHub repository. Note that this project comes in two branches:
  • The delombok branch (also the HEAD revision): A demo setup for use with delombok.
  • The lombok branch: A demo setup for use with lombok.
Let me know in the comments section whether you agree with my assertions, or post what other reservations against Lombok you have found.

You may also be interested in