The Ultimate Lombok Annotations Guide

In this post, we will discuss the different annotations that are offered by Lombok and what are the effects of these annotations on Java classes. If you haven’t already, please make sure to check our guide on how to add Lombok to your Spring boot project. This will help you get up and running with Lombok in your Spring boot (or any other kind of Java) application.

We will classify the different available annotations into three categories. First are the annotations that you could add on your class’ inner variables. Then we will explore method annotations. Finally, we will discuss class level annotations which you can add to your classes.

Inner member annotations

@Getter

The @Getter annotation is used to generate a getter for a field. The annotation can be applied to any inner , static or final field.

public class MyCandlestick {

    @Getter
    private String name;

By default, the generated getter will be public. However, you can change the access level by setting it in the annotation.

public class MyCandlestick {

    @Getter(lombok.AccessLevel.PROTECTED)
    private String name;

The code above will set the getter’s access level to protected.


@Setter

The @Setter annotation behaves the same as the getter. It will generate a setter for the field with public access level. However, it will not work if you add it to a final variable and your IDE will probably complain about that.

    @Setter
    private double close;

You can also set the access level of the generated setter using the access level value.

    @Setter(lombok.AccessLevel.PROTECTED)
    private double close;

@NonNull

The @NonNull annotation will add a null check to any methods that set the field such as setters and constructors. If an attempt to set the field with a null value is performed, a NullPointerException is thrown with the name of the field mentioned in the exception.

    @NonNull
    @Setter
    private String name;

@With

The @With annotation is used to generate a method which makes a copy of the object, but with a modified value of the annotated field. This annotation needs to be combined with an @AllArgsConstructor annotation in order to work (or you could manually create the constructor). For example:

@AllArgsConstructor
public class MyCandlestick {

    @With
    private String name;

    private double close;

}

This will generate a method with the name .withName. This generates a copy of the object, with all fields having the same value except for the field which is annotated with the @With annotation. An example usage of the generated code can be found below.

		MyCandlestick candlestick1 = new MyCandlestick("aabb", 32.3);

		MyCandlestick candlestick2 = candlestick1.withName("bbcc"); //creates a copy of the object but with a modified name

Method level Annotations

@Synchronized

The @Synchronized annotation is a method level annotation. According to Lombok, using this annotation is preferred over using Java’s synchronized keyword because unlike the synchronized keyword which locks on this, the annotation will synchronize the method on an inner variable. If the annotation is added to a static method, then it will lock on a static variable.

The lock object will be generated by the Lombok automatically if you do not explicitly define it.

    @Synchronized
    public void testSync(){
        //....
    }

    Object lockObj = new Object();
    
    @Synchronized("lockObj")
    public void testSync2(){
        //....
    }

In the first method, Lombok will generate a lock with the name $Lock. The second one will use the lockObj inner field as the lock. The equivalent code of the second method is as follows.

    private final Object $lock = new Object[0];

    @Synchronized
    public void testSync2(){
        synchronized($lock) {
            //your code here
        }
    }

Please note that only one lock object will be generated by Lombok for all the inner methods and one lock will be generated for all static methods annotated with the @Synchronized. If you want to use a different lock per method, then you will need to define a different lock object per method.


@SneakyThrows

The @SneakyThrows is an annotation that could be added to a method which throws a checked exception. This allows you to omit adding the “throws …” clause to the method’s signature.

    @SneakyThrows
    public void testSneaky(){
        BufferedReader bufferedReader =
                new BufferedReader(new InputStreamReader(System.in));
        bufferedReader.read();
    }

Any methods which then call this method can use it without having to declare the throws clause, a try and catch or another @SneakyThrows.

	public static void main(String[] args) {


		MyCandlestick candlestick1 = new MyCandlestick("aabb", 32.3);
		candlestick1.testSneaky();

However, you need to be careful when using this annotation as you will no longer be able to write catch clause for these checked exceptions.

try {
			candlestick1.testSneaky();
		}catch (IOException e){ //compiler error which says IOException is never thrown
			
		}

The @SneakyException is one of those Lombok features which you need to use carefully and with a lot of consideration as it might be hard to de-Lombok your code later on if the need arises.

Please also check out the official documentation of this annotation before considering using it.

Class level annotations

@Getter

The @Getter annotation can be added to the class instead of a field. This will cause Lombok to add a getter to each field in the class.

@Getter
public class MyCandlestick {

@Setter

The @Setter annotation will add a setter to each non-final field in the class. An example usage is as follows.

@Setter
public class MyCandlestick {

@NoArgsConstructor

The @NoArgsConstructor adds a zero arguments constructor to the class. It can be used as follows.

@Setter
@NoArgsConstructor
public class MyCandlestick {

You can change the access level of the constructor by configuring it in the annotation.


@NoArgsConstructor(access = AccessLevel.PUBLIC)
public class MyCandlestick {

You also have the option to generate a “static constructor”. This is a static method that encapsulates the original constructor. If you add a static name to the constructor, then Lombok will generate a static method which encapsulates the no args constructor. The generated no-args constructor will be private.


@NoArgsConstructor(access = AccessLevel.PUBLIC, staticName = "of")
public class MyCandlestick {

The static constructor can be used as follows.

		MyCandlestick candlestick = MyCandlestick.of();	// No Args Constructor

@AllArgsConstructor

This annotation will add a constructor with an argument list of the same length and order as the inner fields that exist in the class. For example.

@AllArgsConstructor
public class MyCandlestick {

    private String name;

    private double close;

    private int volume;

This class will have a constructor that can be used as follows.

	MyCandlestick candlestick1 = new MyCandlestick("", 1.2, 2);

Just like the NoArgsConstructor annotation, this annotation can also be used to created a static version of the constructor. This can be done by setting the staticName value.

@AllArgsConstructor(staticName = "foo")
public class MyCandlestick {

@RequiredArgsConstructor

The @RequiredArgsConstructor is a Lombok annotation which will generate a constructor with only the required fields. Required fields are non-initialized final fields and fields annotated with @NonNull. Take for example the following class.


@RequiredArgsConstructor
public class MyCandlestick {

    private String name;

    private double close;

    private final int required1;
    
    @NonNull
    private String required2;

    private static int bla;


}

The generated constructor will contain only arguments for the required1 and required2 fields.

Again, as with all other constructor annotations, you can set the access level or generate a static version of the constructor.


@RequiredArgsConstructor(access = AccessLevel.PACKAGE, staticName = "myStaticName")
public class MyCandlestick {

@Builder

The @Builder annotation is one of those interesting annotations from Lombok as it provides you with a builder pattern interface for the annotated class.

Take for example the following class.

@Builder
public class MyCandlestick {

    private String name;

    private double close;

}

This will generate a builder which can be used to generate an instance of the class. An example usage can be found below.

		MyCandlestick candlestick1 = MyCandlestick.builder()
										.close(3.2)
										.name("hello")
										.build();

The builder class name, access level and other properties are configurable. We will explore these configurations in more detail in a later post.

I would highly recommend that you check out the book “Head First Design Patterns: A Brain Friendly Guide” (Click to check on Amazon)”. This is because every software developer needs to get to know about software design patterns such as the builder pattern, singleton pattern, command pattern, etc.

These patterns serve as best practices which are applied for different situations that you will encounter throughout your development career. Therefore it is important to educate yourself about them in order to evolve into a wise and experienced developer and to build maintainable and clean software applications.


@Singular with @Builder

The @Singular annotation is not a class level annotation. However, we could not introduce it before introducing the @Builder annotation.

This annotation can be added to collections in Java classes and is combined with the @Builder annotation. The result is that Lombok will add builder interface methods that modify that collection. For example:

@Builder
public class MyCandlestick {

    private String name;

    private double close;

    @Singular
    private List<String> snails;

}

This will automatically add the methods .snail(String snail) to add an item to the collection and a .clearSnails() to empty the collection. This will also automatically initialize a new instance of the List. An example usage could look as follows.

		MyCandlestick candlestick1 = MyCandlestick.builder()
							.close(3.2)
							.name("hello")
							.snail("s123")
							.clearSnails()
							.build();

@ToString

This is a class level annotation which could be used to generate a toString method. By default, the method includes all fields of the class. However, individual fields could be excluded using the exclude property of the annotation. For example:

@ToString(exclude = {"close"})
public class MyCandlestick {
    
    private String name;

    private double close;

}

Let us call the toString method of this class:

MyCandlestick candlestick1 = new MyCandlestick("aabb", 32.3);
String x = candlestick1.toString();

The value of x here will be:

"MyCandlestick(name=aabb)"

@EqualsAndHashCode

The @EqualsAndHashCode annotation is added to the class to generate an .equals(..) and a .hashCode() methods. Just like the @ToString annotation, you could exclude fields using the exclude property. For example:

@EqualsAndHashCode(exclude = {"name"})
public class MyCandlestick {

    private String name;

    private double close;

}

You can also configure the annotation by instructing it to include only the fields which are annotated with an @EqualsAndHashCode.Include annotation.

@EqualsAndHashCode(onlyExplicitlyIncluded = true)
public class MyCandlestick {

    private String name;

    @EqualsAndHashCode.Include
    private double close;

}

This will generate the methods with only the “close” variable.


@Data

The @Data annotation is kind of an all in one package. It is equivalent to adding the annotations @Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode to your class.


@Data
public class MyCandlestick {

Next steps

In this post, we discussed the most widely used annotations from Lombok. However, there are some annotations which we have not discussed such as the @CleanUp and @Value. This is because these annotations deserve a post of their own.

Also note that this post serves as an introduction to the most essential annotations in Lombok. However, in real world applications, you will encounter situations which were not covered in this tutorial. For example, what happens when inheriting from a Lombok annotated classes? Can you override annotations from Superclasses? etc..

Therefore, please stay posted by following us on twitter and subscribing to our newsletter in order to be notified of the latest posts about Lombok.

The Ultimate Lombok Annotations Guide

Leave a Reply

Your email address will not be published. Required fields are marked *

Scroll to top