Thursday, June 23, 2016

Lombok, AutoValue, and Immutables

I liked Brandon's suggestion of a blog post comparing Project Lombok, AutoValue, and Immutables and this is a post that attempts to do that. I have covered Project Lombok, AutoValue, and Immutables individually with brief overviews, but this post is different in that it highlights the similarities and differences between them.

Lombok, AutoValue, and Immutables share quite a bit in common and I try to summarize these similarities in this single descriptive sentence: Lombok, AutoValue, and Immutables use annotation processing to generate boilerplate code for common operations used by value object classes. The remainder of this post looks at these similarities in more detail and contrasts the three approaches.

Code Generation

Lombok, AutoValue, and Immutables are all designed to generate verbose boilerplate code from concise code representations that focus on the high-level business logic and leave low-level details of implementation to the code generation. Common object methods such as toString(), equals(Object), and hashCode() are important but need to be written correctly. It is easy to make mistakes with these and even when they are written correctly originally (including via IDE generation), they can be neglected when other changes are made to the class that impact them.

Value Objects

Lombok, AutoValue, and Immutables each support generation of "value objects." While AutoValue strictly enforces generation of value objects, Immutables allows generated objects to be modifiable if @Modifiable is specified, and Lombok supports multiple levels of modification in its generated classes with annotations such as @Set and @Data.

Beyond Value Objects

AutoValue is focused on generation of value objects and supports generation of fields, constructor/builder, concrete accessor methods, and implementations of common methods equals(Object), hashCode(), and toString() based on the abstract methods in the template class.

Immutables provides capability similar to that provided by AutoValue and adds the ability to generate modifiable classes with @Value.Modifiable. Immutables also offers additional features that include:

Lombok provides value class generation capability similar to AutoValue with the @Value annotation and provides the ability to generate modifiable classes with the @Data annotation. Lombok also offers additional features that include:

Based on Annotations Processing

Lombok, AutoValue, and Immutables all generate more verbose boilerplate code from more concise template code via annotations processing. Each includes a javax.annotation.processing.Processor defined in its JAR file's META-INF/services area as part of the standard annotation processor discovery process that is part of the javac compiler.

Not All Annotation Processing is the Same

Although Lombok, AutoValue, and Immutables all employ annotation processing via javac, the particulars of how Lombok uses annotation processing are different than how AutoValue and Immutables do it. AutoValue and Immutables use annotation processing in the more conventional sense and generate source from source. The class source code generated by AutoValue and Immutables is not named the same as the template class and, in fact, extends the template class. AutoValue and Immutables both read the template class and generate an entirely new class in Java source with its own name that has all the generated methods and fields. This avoids any name collisions with the template class and makes it fairly easy to mix the template class source code and generated class source code in the same IDE project because they are in fact different classes.

AutoValue's Generation via Annotation Processing

Immutables's Generation via Annotation Processing

Lombok approaches generation via annotations processing differently than AutoValue and Immutables do. Lombok generates a compiled .class file with the same class name as the "template" source code and adds the generated methods to this compiled version. A developer only sees the concise template code when looking at .java files, but sees the compiled .class file with methods not present in the source code when looking at the .class files. The generation by Lombok is not of another source file but rather is of an enhanced compiled version of the original source. There is a delombok option one can use with Lombok to see what the generated source behind the enhanced .class file looks like, but the project is really designed to go straight from concise template source to enhanced compiled class without need or use for the intermediate enhanced source file. The delombok option can be used to see what the generated source would look like or, perhaps more importantly, can be used in situations where it is confusing to the tools to have inconsistent source (concise template .java file) and generated class (enhanced .class file of same name) in the same space.

Lombok's Generation via Annotation Processing

Lombok's approach to annotation processing is less conventional than the approach AutoValue and Immutables employ and some, including Lombok's creator, have called the approach "a hack." A good explanation of the Lombok "trick" or "hack" is contained in neildo's post Project Lombok - Trick Explained, which cites the also informative OpenJDK Compilation Overview.

The main reasons for the controversy surrounding Lombok's approach are closely related and are that it uses non-standard APIs and, because of this, it can be difficult to integrate well with IDEs and other tools that perform their own compilation (such as javadoc). Because AutoValue and Immutables naturally generate source code with new class names, any traditional tools and IDEs can work with the generated source alongside the template source without any major issues.

Summary of Similarities and Differences

Characteristic Project Lombok AutoValue Immutables Comments
Covered Version 1.16.8 (2016) 1.2 (2016) 2.2.8 (2016) Version used for this post
My Overview 2010 2016 2016  
Year Originated 2009 2014 2014  
License MIT (also) Apache 2 Apache 2 All open source
Minimum Java 1.6 1.6 1.7 Oldest supported Java version
Dependencies ASM (for Eclipse integration) ASM (Optional) Runtime Dependency: Guava Libraries dependent upon (included) at compile time
javax.annotation.processing.Processor lombok.launch.AnnotationProcessorHider$AnnotationProcessor com.google.auto.value.processor.AutoAnnotationProcessor
com.google.auto.value.processor.AutoValueBuilderProcessor
com.google.auto.value.processor.AutoValueProcessor
org.immutables.processor.ProxyProcessor Standard annotation processor specification location
Generated Source Relationship to Template Source Enhanced generated class replaces template source Generated source extends template source Lombok only shows generated source with "delombok" option
Access Generated Source Specify delombok option Default Default To view/control generated source code
Generated Methods equals(Object), hashCode(), toString(), construction/builder, accessors, setters equals(Object), hashCode(), toString(), construction/builder, accessors equals(Object), hashCode(), toString(), construction/builder, accessors, setters
Degree of Immutability Allows full mutability with field-level @Set but provides @Value when immutability is desired Enforces strict immutability "Heavily biased towards immutability" but provides class-level @Value.Modifiable AutoValue is most opinionated and Lombok is least opinionated
Bonus Features Resource cleanup
Immutable or Mutable
Sneakily thrown checked exceptions
Object synchronization locks
Logging annotation
More ...
Faithfulness to Value Object concept
Documented Best Practices
Style customization
Serialization (including JSON)
Pre-computed hash codes
More...
 

Considerations When Choosing

Lombok, AutoValue, and Immutables are similar toolkits that provide similar benefits and any of these three could be used successfully by a wide range of applications. However, there are differences between these toolkits that can be considered when selecting which of them to use.

  • Lombok generates a class with the same package and class name as the template while AutoValue and Immutables generate classes that extend the template class and have their own class name (but same package).
    • Developers who would like the compiled .class file to have exactly the same package and name as the template class will prefer Lombok.
    • Developers who prefer the generated source code always be available and not in conflict in any way with the template source will prefer AutoValue or Immutables.
  • AutoValue is the most opinionated of the three toolkits and Lombok tends to be the least opinionated.
    • Developers wanting the tight enforcement of characteristics of "value objects" are likely to prefer AutoValue. AutoValue does not provide a mechanism for generated classes to be modifiable and enforces several other rules that the other two toolkits do not enforce. For example, AutoValue only allows the template class to be expressed as an abstract class and not as an interface to avoid "[losing] the immutability guarantee ... and ... [inviting] more ... bad behavior." Immutables, on the other hand, does allow interfaces to be used as the templates for code generation.
    • Developers who want to depart from strict immutability or use some of the features AutoValue does not support in the interest of best practices opinions will likely prefer Immutables or Lombok.
  • AutoValue and Immutables use standard annotations processing and Lombok uses a non-standard annotations processing approach.
    • Developers wishing to avoid non-standard dependencies will favor AutoValue or Immutables.
    • Developers wanting to avoid IDE plugins or other special tools outside of javac and basic Java IDE support will favor AutoValue or Immutable.
  • All three toolkits support some level of customization and developers wishing to customize the generated code may want to choose the toolkit that allows them to customize the generated code in the ways they desire.
    • Lombok provides a configuration system that allows for several aspects of the generated code to be adjusted to desired conventions.
    • Immutables provides style customization that allows for several aspects of the generated code to be adjusted to desired conventions.
    • The How Do I? section of AutoValue's User Guide spells out some approaches to customize the code AutoValue generates (typically via use or avoidance of keywords in the template class).
  • AutoValue and Lombok are supported on JDK 1.6, but Immutables requires JDK 1.7.

Conclusion

Lombok, AutoValue, and Immutables share much in common and all three can be used to generate value classes from simple template files. However, they each also offer different advantages and features that may make any one of them more or less appealing to developers than the others based on the developers' individual circumstances.

5 comments:

jmmk said...

> Developers who prefer the generated source code always be available and not in conflict in anyway with the template source will prefer AutoValue or Lombok.

should read "will prefer AutoValue or Immutables".

@DustinMarx said...

Thanks jmmk,

I have fixed that.

Dustin

TheAlchemist said...

Gotta say that Immutables is my favorite. :) Been using it for a while now.

@DustinMarx said...

Stephen Colebourne has posted Code generating beans - mutable and immutable, a post that briefly compares different bean code generators including AutoValue, Immutables, and Lombok.

Unknown said...

Helpful post, thanks. I've only used AutoValue before and have been very happy with it. Immutables looks interesting, but Lombok feels a bit too 'magical' for me and poor IDE support is a deal-breaker for me.

It might be worth mentioning that AutoValue now supports extensions, which make it possible to customise the generated classes in various ways. e.g. https://github.com/gabrielittner/auto-value-with adds support for "with-er" methods, which make it simple to create mutated copies of immutable objects.