Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for ☕️ Immutables/AutoValue/Lombok 🔥 Which One?
Carlos Chacin ☕👽
Carlos Chacin ☕👽

Posted on • Edited on

     

☕️ Immutables/AutoValue/Lombok 🔥 Which One?

The article was initially published atcarloschac.in

See also:

In this article, we are going to compare some of the features of theImmutables.org library,Google AutoValue andProject Lombok:

  • Generated theBuilder pattern by default?
  • Generated helper methods for, i.e.,Optional andList?
  • 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 andtoString methods
  • other utilities

NOTE:Immutables andAutoValue generate new classes with the processor, andLombok modifies thebytecode of the original class.

💡 Overview

Immutables 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 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 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.

Optional<Integer>myOptional;StringmyString;List<String>myList;
Enter fullscreen modeExit fullscreen mode

🔩 Creating the model withAutoValue

packageautovalue;importcom.google.auto.value.AutoValue;importjava.util.List;importjava.util.Optional;@AutoValuepublicabstractclassMyModel{publicabstractOptional<Integer>myOptional();publicabstractStringmyString();publicabstractList<String>myList();// Builder not generated by default// We have to write this boilerplate code@AutoValue.BuilderpublicabstractstaticclassBuilder{publicabstractBuildersetMyOptional(Optional<Integer>myOptional);publicabstractBuildersetMyString(StringmyString);publicabstractBuildersetMyList(List<String>myList);publicabstractMyModelbuild();}}
Enter fullscreen modeExit fullscreen mode
  • Generated theBuilder pattern by default? 🔴
  • Generated helper methods for, i.e.,Optional andList? 🔴 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 withLombok

packagelombok;importjava.util.List;importjava.util.Optional;@Value@BuilderpublicclassMyModel{Optional<Integer>myOptional;StringmyString;List<String>myList;}
Enter fullscreen modeExit fullscreen mode
  • Generated theBuilder pattern by default? 🔴
  • Generated helper methods for, i.e.,Optional andList? 🔴 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 withImmutables

packageimmutables;importorg.immutables.value.Value;importjava.util.List;importjava.util.Optional;@Value.ImmutablepublicinterfaceMyModel{Optional<Integer>myOptional();StringmyString();List<String>myList();}
Enter fullscreen modeExit fullscreen mode
  • Generated theBuilder pattern by default? ✅
  • Generated helper methods for, i.e.,Optional andList? ✅, 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:

list1=List.of("OneValue")list2=List.of("OneValue")
Enter fullscreen modeExit fullscreen mode

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

MyModel1:(myOptional=Optional.of(1)myString="Hello"myList=list1// Using list 1)MyModel2:(myOptional=Optional.of(1)myString="Hello"myList=list2// Using list 2)
Enter fullscreen modeExit fullscreen mode

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

model1==model2// TRUE
Enter fullscreen modeExit fullscreen mode

After mutating one of the lists, the comparison of the objects should be the same

list1.add("AnotherValue")model1==model2// TRUE
Enter fullscreen modeExit fullscreen mode

🍭 TestingAutoValue

@Testvoidimmutability(){// Create 2 lists containing the same elementvarmyList1=newArrayList<String>();myList1.add("OneValue");varmyList2=List.of("OneValue");// Create model 1, assigning the list1varmyModel1=newAutoValue_MyModel.Builder().setMyOptional(Optional.of(1))// 😥 🔴 No helper for Optional.setMyString("Hello").setMyList(myList1)// 😥 🔴 No helper for List.build();// Create model 2, assigning the list2varmyModel2=newAutoValue_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 valuesassertThat(myModel1).isEqualTo(myModel2);// Mutate the list used on Model 1myList1.add("AnotherValue");// Compare the 2 objects:// - PASSES objects are NOT equal for AutoValue 😮 🔴assertThat(myModel1).isNotEqualTo(myModel2);}
Enter fullscreen modeExit fullscreen mode

🍭 TestingLombok

@Testvoidimmutability(){// Create a mutable list with 1 elementvarmyList1=newArrayList<String>();myList1.add("OneValue");varmyList2=List.of("OneValue");// Create model 1, assigning the list1varmyModel1=MyModel.builder().myOptional(Optional.of(1))// 😥 🔴 No helper for Optional.myString("Hello").myList(myList1)// 😥 🔴 No helper for List.build();// Create model 2, assigning the list2varmyModel2=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 valuesassertThat(myModel1).isEqualTo(myModel2);// Mutate the list used on Model 1myList1.add("AnotherValue");// Compare the 2 objects:// - PASSES objects are NOT equal for Lombok 😮 🔴assertThat(myModel1).isNotEqualTo(myModel2);}
Enter fullscreen modeExit fullscreen mode

🍭 TestingImmutables

@Testvoidimmutability(){// Create a mutable list with 1 elementvarmyList1=newArrayList<String>();myList1.add("OneValue");varmyList2=List.of("OneValue");// Create model 1, assigning the list1varmyModel1=ImmutableMyModel.builder().myOptional(1)// 🎩 ✅ Helper for Optional.myString("Hello").myList(myList1).build();// Create model 2, assigning the list2varmyModel2=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 valuesassertThat(myModel1).isEqualTo(myModel2);// Mutate the list used on Model 1myList1.add("AnotherValue");// Compare the 2 objects:// - Test PASSES objects ARE EQUAL for Immutables 🎩 ✅assertThat(myModel1).isEqualTo(myModel2);}
Enter fullscreen modeExit fullscreen mode

📈 Results

AutoValueLombokImmutables
Line of Code to write/maintain281415
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:


🔆 Conclusions

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

  • To be fair, bothLombok andAutoValue can achieve also immutability but it requires paying more attention when creating the classes and that can cause problems.

  • One main advantage ofAutoValue is that it generates less code and that would be convenient if you are developing on/forAndroid.

  • The configuration options forAutoValue andLombok are pretty limited compared withImmutables 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.

Top comments(11)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
josealonso profile image
José Ramón (JR)
Senior Software Engineer based in Spain.

You might want to check thevavr functional library. It has monads like Scala and it doesn't use annotations.

CollapseExpand
 
caneminops profile image
Canem Inops
  • Joined

Hey Carlos,

From the final table I feel that your method to compare these libraries were: get all features that I care in Immutables and compare with the others.
It is your space and you have the right, but I don't think that is fair to the readers.

I'm more familiar with Lombok and there are some goods parts that are absent here. For instance?

  • If I don't want a builder? If my class has 3 attributes I don't really need a builder. How do I do that with the others? Will still be the same amount of boiler plate?

  • About "helper for copying": an builder in Lombok creates a toBuilder method that allows you to make a builder with all values, change the ones you want and rebuild the object.

  • Having an abstract class/interface defining your model, how these play together with jackson or hibernate where you have to annotate your class, how does it work?

  • What about if my fields are made of mutable fields, will Immutable still be immutable by default?

I don't even know the answer for all these questions but I think a fairer comparison would try to go beyond the set of features that your favorite library offers.

CollapseExpand
 
cchacin profile image
Carlos Chacin ☕👽
Java Dev | Husband | Father-------
  • Location
    Seattle, WA
  • Education
    Computer Science
  • Work
    Software Developer Engineer
  • Joined

Hi Canem,

Thank you for reading the post and adding your comments and feedback.

As mentioned in the post, this is a comparison of some of the three libraries' features, not an extensive review of every single feature.

In this article, we will compare some of the features of the Immutables.org library, Google AutoValue, and Project Lombok:

The particular set of evaluated features is based on the features that I consider essential for my use cases and are also defined at the beginning of the post.

Answering your question below:

If I don't want a builder? If my class has 3 attributes I don't really need a builder. How do I do that with the others? Will still be the same amount of boiler plate?

Immutables is even more flexible than Lombok in that case, allowing you to choose between a simple constructor with the parameters, but also a static factory method

  • About "helper for copying": an builder in Lombok creates a toBuilder method that allows you to make a builder with all values, change the ones you want and rebuild the object.

Good catch, Even when I do not evaluate that in the comparison, I'll remove the java comment in the lombok example.

  • Having an abstract class/interface defining your model, how these play together with jackson or hibernate where you have to annotate your class, how does it work?

For Hibernate/JPA, I do not use these libraries, IIRC the JPA standard requires JavaBeans mutable objects. But I do use them in particular for De/Serialization with Jackson, Jsonb, Gson, etc. and those play well with Interfaces and abstract classes.

  • What about if my fields are made of mutable fields, will Immutable still be immutable by default?

Yes, and that is one of the critical aspects of the library, AutoValue and Lombok generate shallowly immutable classes.

There are a lot of additional features in the Immutables library that I consider useful. Lombok and AutoValue are not providing those, but I agree with the fact that it depends on the need and uses cases.
The three libraries work pretty well, and if you don't need any additional features from the immutables library, AutoValue and Lombok are also valid options.

CollapseExpand
 
cchacin profile image
Carlos Chacin ☕👽
Java Dev | Husband | Father-------
  • Location
    Seattle, WA
  • Education
    Computer Science
  • Work
    Software Developer Engineer
  • Joined

I just checked the Lombok generated code and I cannot see the utility method for copying/cloning:

$javap MyModel$MyModelBuilder
publicclasslombok2.MyModel$MyModelBuilder{lombok2.MyModel$MyModelBuilder();publiclombok2.MyModel$MyModelBuildermyOptional(java.util.Optional<java.lang.Integer>);publiclombok2.MyModel$MyModelBuildermyString(java.lang.String);publiclombok2.MyModel$MyModelBuildermyList(java.util.List<java.lang.String>);publiclombok2.MyModelbuild();publicjava.lang.StringtoString();}

Can you point me to an example or documentation around that?

Thread Thread
 
mmiikkkkaa profile image
Mikka
  • Joined

Its some time now, but maybe it still helps. The copy-function of lombok is@With, which creates a copy with one property changed.

For your example it might be something like
myModel1.withMyString("other string")
which will create a copy ofmyModel1 with the changedmyString value.

Thats what I also like about lombok, that you can use just the parts you need. Immutable is hard to use for legacy code, since you need to adapt all at once, while lombok leaves you with the chance to enhance classes with just the stuff you need. Copy a class? Add@With to it. Want a builder? Add@Builder to a class. Get rid of existing getters and setters? Just add@Getter and/or@Setter to it and delete whats already there.

CollapseExpand
 
mmiikkkkaa profile image
Mikka
  • Joined

With lombok you could still generate code for JPA entities, since you can chose what you want to use, e.g. getters, setters, constructors, etc. Of cause there arepitfalls to watch out for, but it is usable, since it doesn't turn it immutable automatically.

CollapseExpand
 
am312 profile image
am312
  • Joined

In Testing Immutables you're not comparing a mutated myList1 vs myList2 like you did in the previous tests.

CollapseExpand
 
cchacin profile image
Carlos Chacin ☕👽
Java Dev | Husband | Father-------
  • Location
    Seattle, WA
  • Education
    Computer Science
  • Work
    Software Developer Engineer
  • Joined

Hi am312, good catch, that variable is not actually used in the Immutables example, I'll update the example to remove that. Thanks

CollapseExpand
 
am312 profile image
am312
  • Joined

in order to do an apples-to-apples comparison, shouldn't you be using it? I guess you're trying to demonstrate if the libraries are doing a deep comparison of the collections' contained objects or just testing for ==.

CollapseExpand
 
spartanhooah profile image
Ben Frey
Java developer
  • Location
    San Antonio, TX
  • Education
    MS un Computer Science from Michigan State University
  • Work
    Software Engineer I at USAA
  • Joined

I'm curious why you expect two objects which contain different lists to be equal? That is certainly not intuitive. Does Immutables allow for configuring deep equals comparisons?

CollapseExpand
 
endouakulu profile image
Eric Ndouakulu Kiaku Mbuta
Works with JAVA, Kotlin, Typescript, Go Craftmanship as way of life TDD & DDD enthusiast Hexagonal Architecture addict
  • Location
    Paris
  • Work
    Team Leader / Tech Leader
  • Joined

Hey Carlos,

Why you don't test JAVA 14 record feature ?

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Java Dev | Husband | Father-------
  • Location
    Seattle, WA
  • Education
    Computer Science
  • Work
    Software Developer Engineer
  • Joined

More fromCarlos Chacin ☕👽

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp