• notice
  • Congratulations on the launch of the Sought Tech site

Lombok uses @With annotation

1. Introduction

Lombok is a library that helps us significantly reduce boilerplate code when writing Java applications.

In this tutorial, we will see how to use this library to make copies of immutable objects that only change a single property.

2. Usage

When using immutable objects that do not allow setters, we may need an object similar to the current object, but with only one attribute different. This can be achieved using Lombok's @Withannotations:

public class User { private final String username; private final String emailAddress; @With
 private final boolean isAuthenticated;
 //getters, constructors

The above comments generate the following in the background:

public class User { private final String username; private final String emailAddress; private final boolean isAuthenticated;
 //getters, constructors
 public User withAuthenticated(boolean isAuthenticated) { return this.isAuthenticated == isAuthenticated ? this : new User(this.username, this.emailAddress, isAuthenticated);

Then we can use the method generated above to create a mutated copy of the original object:

User immutableUser = new User("testuser", "[email protected]", false);
 User authenticatedUser = immutableUser.withAuthenticated(true);
 assertNotSame(immutableUser, authenticatedUser);

In addition, we can choose to annotate the entire class, which will have all property withX()methods .

3. Requirements

To @Withcomment, we need to provide a full-parameter constructor . From the above example, we can see that the generated method needs this to create a clone of the original object.

We can use Lombok's own @AllArgsConstructoror @Valueannotations to meet this requirement. Alternatively, we can provide this constructor manually while ensuring that the order of non-static properties in the class matches the order of the constructor.

We should remember that if you use @With** annotations on static fields , you will do nothing. This is because static properties are not considered part of the object state. In addition, Lombok will skip$ the method generation of the field ** at the beginning of the symbol.

4. Advanced usage

Let's examine some advanced scenarios when using this annotation.

4.1. Abstract class

We can use the abstract field@With

public abstract class Device { private final String serial; @With
 private final boolean isInspected;
 //getters, constructor

However, we need towithInspected() provide an implementation for the generated method . This is because Lombok doesn't know the concrete implementation of our abstract class to create its clone:

public class KioskDevice extends Device {
 public Device withInspected(boolean isInspected) { return new KioskDevice(getSerial(), isInspected);
 //getters, constructor

4.2. Naming Convention

As mentioned above, Lombok will skip $fields that start with a symbol. However, if the field starts with a character, it is the case of the title, and finally, withthe prefix of the generation method.

Or, if the field starts with an underscore, it is withonly used as a prefix for the generation method:

public class Holder { @With
 private String variableA; @With
 private String _variableB; @With
 private String $variableC;
 //getters, constructor excluding $variableC

According to the code above, we see that only the first two variables will be generated for themwithX()

Holder value = new Holder("a", "b");
 Holder valueModifiedA = value.withVariableA("mod-a");
 Holder valueModifiedB = value.with_variableB("mod-b"); // Holder valueModifiedC = value.with$VariableC("mod-c"); not possible

4.3. Exceptions generated by the method

We should note that, except for the fields beginning with $symbols , if there are already withX()methods in our class , Lombok will not generate it:

public class Stock { @With
 private String sku; @With
 private int stockCount;
 //prevents another withSku() method from being generated
 public Stock withSku(String sku) { return new Stock("mod-" + sku, stockCount);

In the above scenario, no new withSku()methods will be generated .

In addition, Lombok will skip method generation in the following scenarios :

public class Stock { @With
 private String sku; private int stockCount;
 //also prevents another withSku() method from being generated
 public Stock withSKU(String... sku) { return sku == null || sku.length == 0 ? new Stock("unknown", stockCount) : new Stock("mod-" + sku[0], stockCount);

We can notice the abovewithSKU()

Basically, Lombok will skip method generation if:

  • The same method exists as the generated method name (ignoring case)

  • The existing method and the generated method have the same number of parameters (including var-args)

4.4. Null verification of the generated method

Similar to other Lombok annotations, we can @Withannotate the generated methodnull

 public class ImprovedUser { @NonNull
 private final String username; @NonNull
 private final String emailAddress;

Lombok will generate the following code for us and the required nullchecks:

public ImprovedUser withUsername(@NonNull String username) { if (username == null) { throw new NullPointerException("username is marked non-null but is null");
 } else { return this.username == username ? this : new ImprovedUser(username, this.emailAddress);
 public ImprovedUser withEmailAddress(@NonNull String emailAddress) { if (emailAddress == null) { throw new NullPointerException("emailAddress is marked non-null but is null");
 } else { return this.emailAddress == emailAddress ? this : new ImprovedUser(this.username, emailAddress);

5 Conclusion

In this article, we have seen how to use Lombok's @Withannotations to generate clones of specific objects with individual field changes.

We also learned how and when this method actually works, and how nullto enhance it with other verifications (such as inspections).


Technical otaku

Sought technology together

Related Topic


Leave a Reply