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:
- Singleton instances
- Precomputed hash code
- Instance interning
- Customizable equals(Object), hashCode(), and toString() implementations including exempting fields from them
- Basic and advanced serialization
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 |
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.
- Developers who would like the compiled
- 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 aninterface
to avoid “[losing] the immutability guarantee … and … [inviting] more … bad behavior.” Immutables, on the other hand, does allowinterface
s 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.
- 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
- 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.
Reference: | Lombok, AutoValue, and Immutables from our JCG partner Dustin Marx at the Inspired by Actual Events blog. |