☕️ Immutables/AutoValue/Lombok 🔥 Which One?
Tags: java, bestpractices, immutability
Published: 2020-04-13 03:42:53

---
title: ☕️ Immutables/AutoValue/Lombok 🔥 Which One?
description: In this article, we are going to compare some of the features of the Immutables.org library, Google AutoValue, and Project Lombok.
tags: java, best-practices, immutability
cover_image: https://carloschac.in/public/images/OfficeDesk.png
series: Immutability in Java
---

> The article was initially published at [carloschac.in](https://carloschac.in/2020/04/12/immutables-autovalue-lombok/)

See also:

{% post https://dev.to/cchacin/immutability-in-java-made-easy-372g %}

In this article, we are going to compare some of the features of the [Immutables.org](http://immutables.github.io/) library, [Google AutoValue](https://github.com/google/auto) and [Project Lombok](https://projectlombok.org/):

- Generated the `Builder` pattern by default?
- Generated helper methods for, i.e., `Optional` and `List`?
- The number of lines of code to write?
- Required IDE's plugins?
- Are the objects immutable?

The three libraries are based on an annotation processor to generate/modify code for us:

- Immutable classes
- `equals`, `hashCode` and `toString` methods
- other utilities

**NOTE:** `Immutables` and `AutoValue` generate new classes with the processor, and `Lombok` modifies the *bytecode* of the original class.

<!-- more -->

## 💡 Overview

> [Immutables](http://immutables.github.io/) Java annotation processors to generate simple, safe, and consistent value objects. Do not repeat yourself, try Immutables, the most comprehensive tool in this field!

> [AutoValue](https://github.com/google/auto) provides an easier way to create immutable value classes, with a lot less code and less room for error, while not restricting your freedom to code almost any aspect of your class exactly the way you want it.

> [Project Lombok](https://projectlombok.org/) is a java library that automatically plugs into your editor and build tools, spicing up your java.
Never write another getter or equals method again, with one annotation your class has a fully-featured builder, Automate your logging variables, and much more.

## 📇 The Model

We are going to create a class using the three libraries to be able to create an object representation with the following fields.

```java
Optional<Integer> myOptional;
String myString;
List<String> myList;
```

### 🔩 Creating the model with `AutoValue`

```java
package autovalue;

import com.google.auto.value.AutoValue;

import java.util.List;
import java.util.Optional;

@AutoValue
public abstract class MyModel {
    public abstract Optional<Integer> myOptional();

    public abstract String myString();

    public abstract List<String> myList();

    // Builder not generated by default
    // We have to write this boilerplate code
    @AutoValue.Builder
    public abstract static class Builder {
        public abstract Builder setMyOptional(Optional<Integer> myOptional);

        public abstract Builder setMyString(String myString);

        public abstract Builder setMyList(List<String> myList);

        public abstract MyModel build();
    }
}
```

- Generated the `Builder` pattern by default? 🔴
- Generated helper methods for, i.e., `Optional` and `List`? 🔴 Let's see in the next section
- The number of lines of code to write? 28 ✅
- It requires IDE plugin 🔴
- Are the objects immutable? 🔴 Let's see in the next section

### 🔩 Creating the model with `Lombok`

```java
package lombok;

import java.util.List;
import java.util.Optional;

@Value
@Builder
public class MyModel {
    Optional<Integer> myOptional;

    String myString;

    List<String> myList;
}
```

- Generated the `Builder` pattern by default? 🔴
- Generated helper methods for, i.e., `Optional` and `List`? 🔴 Let's see in the next section
- The number of lines of code to write? 14 ✅
- It requires IDE plugins 🔴
- Are the objects immutable? 🔴 Let's see in the next section

### 🔩 Creating the model with `Immutables`

```java
package immutables;

import org.immutables.value.Value;

import java.util.List;
import java.util.Optional;

@Value.Immutable
public interface MyModel {
    Optional<Integer> myOptional();

    String myString();

    List<String> myList();
}
```

- Generated the `Builder` pattern by default? ✅
- Generated helper methods for, i.e., `Optional` and `List`? ✅, Let's see in the next section
- The number of lines of code to write? 15 ✅
- It doesn't require IDE plugin ✅
- Are the objects immutable? ✅, Let's see in the next section

## 🌵Tests

Let's check some Pseudo-code:

We are going to create two identical lists with the same element inside:

```java
list1=List.of("OneValue")
list2=List.of("OneValue")
```

We are going to create two identical value objects like this:

```java
MyModel1: (
  myOptional=Optional.of(1)
  myString="Hello"
  myList=list1 // Using list 1
)

MyModel2: (
  myOptional=Optional.of(1)
  myString="Hello"
  myList=list2 // Using list 2
)
```

Even when using different references for the lists, the objects should be equal by value.

```java
model1 == model2 // TRUE
```

After mutating one of the lists, the comparison of the objects should be the same
```java
list1.add("AnotherValue")

model1 == model2 // TRUE
```

### 🍭 Testing `AutoValue`

```java
@Test
void immutability() {
    // Create 2 lists containing the same element
    var myList1 = new ArrayList<String>();
    myList1.add("OneValue");
    var myList2 = List.of("OneValue");

    // Create model 1, assigning the list1
    var myModel1 = new AutoValue_MyModel.Builder()
            .setMyOptional(Optional.of(1)) // 😥 🔴 No helper for Optional
            .setMyString("Hello")
            .setMyList(myList1) // 😥 🔴 No helper for List
            .build();

    // Create model 2, assigning the list2
    var myModel2 = new AutoValue_MyModel.Builder() // 😥 🔴 No helper for copying
            .setMyOptional(Optional.of(1))
            .setMyString("Hello")
            .setMyList(myList2)
            .build();

    // Compare the 2 objects
    // Test passes since the fields contain the same values
    assertThat(myModel1).isEqualTo(myModel2);

    // Mutate the list used on Model 1
    myList1.add("AnotherValue");

    // Compare the 2 objects:
    // - PASSES objects are NOT equal for AutoValue 😮 🔴
    assertThat(myModel1).isNotEqualTo(myModel2);
}
```

### 🍭 Testing `Lombok`

```java
@Test
void immutability() {
    // Create a mutable list with 1 element
    var myList1 = new ArrayList<String>();
    myList1.add("OneValue");
    var myList2 = List.of("OneValue");

    // Create model 1, assigning the list1
    var myModel1 = MyModel.builder()
            .myOptional(Optional.of(1)) // 😥 🔴 No helper for Optional
            .myString("Hello")
            .myList(myList1) // 😥 🔴 No helper for List
            .build();

    // Create model 2, assigning the list2
    var myModel2 = MyModel.builder() 😥 🔴 // No helper for copying
            .myOptional(Optional.of(1))
            .myString("Hello")
            .myList(myList2)
            .build();

    // Compare the 2 objects
    // Test passes since the fields contain the same values
    assertThat(myModel1).isEqualTo(myModel2);

    // Mutate the list used on Model 1
    myList1.add("AnotherValue");

    // Compare the 2 objects:
    // - PASSES objects are NOT equal for Lombok 😮 🔴
    assertThat(myModel1).isNotEqualTo(myModel2);
}
```

### 🍭 Testing `Immutables`

```java
@Test
void immutability() {
   // Create a mutable list with 1 element
   var myList1 = new ArrayList<String>();
    myList1.add("OneValue");
    var myList2 = List.of("OneValue");

   // Create model 1, assigning the list1
   var myModel1 = ImmutableMyModel.builder()
           .myOptional(1) // 🎩 ✅ Helper for Optional
           .myString("Hello")
           .myList(myList1)
           .build();

   // Create model 2, assigning the list2
   var myModel2 = ImmutableMyModel.builder()
           .from(myModel1) // 🎩 ✅ Helper for copying
           .addMyList("OneValue") // 🎩 ✅ Helper for List
           .build();

   // Compare the 2 objects
   // Test passes since the fields contain the same values
   assertThat(myModel1).isEqualTo(myModel2);

   // Mutate the list used on Model 1
   myList1.add("AnotherValue");

   // Compare the 2 objects:
   // - Test PASSES objects ARE EQUAL for Immutables 🎩 ✅
   assertThat(myModel1).isEqualTo(myModel2);
}
```

## 📈 Results

|                                | AutoValue | Lombok | Immutables |
|:-------------------------------|:---------:|:------:|:----------:|
| Line of Code to write/maintain |    28     |   14   |     15     |
| Builder (by default)           |    🔴     |   🔴   |     ✅      |
| Required IDE Plugin            |     ✅     |   🔴   |     ✅      |
| Immutability                   |    🔴     |   🔴   |     ✅      |
| Helper for Optional            |    🔴     |   🔴   |     ✅      |
| Helper for Collections         |    🔴     |   🔴   |     ✅      |
| Helper for Copying             |    🔴     |   🔴   |     ✅      |

The code and the tests for the above examples are available on GitHub:

{% github cchacin/immutables-autovalue-lombok %}

## 🔆 Conclusions

- Even when the three libraries are doing a great job to avoid the boilerplate code, I personally use the `Immutables` library in most of the projects because of the safe defaults.

- To be fair, both `Lombok` and `AutoValue` can achieve also immutability but it requires paying more attention when creating the classes and that can cause problems.

- One main advantage of `AutoValue` is that it generates less code and that would be convenient if you are developing on/for `Android`.

- The configuration options for `AutoValue` and `Lombok` are pretty limited compared with `Immutables` but that topic was not covered in this article.

- Lombok requires a plugin if you want to be able to see all the modified/added methods to the bytecode.