Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Overview

Project Lombok reduces boilerplate code in Java applications by providing annotations that automatically generate commonly used code.

In this tutorial, we’ll explore the differences between the three constructor annotations offered by this library.

2. Setup

To highlight these differences, let’s begin by adding lombok to our dependencies:

<dependency>
    <groupId>org.projectlombok</groupId> 
    <artifactId>lombok</artifactId> 
    <version>1.18.30</version>
    <scope>provided</scope>
</dependency>

Next, let’s create a class to serve as the basis for our demonstration:

public class Person {
    private int age;
    private final String race;
    @NonNull
    private String name;
    private final String nickname = "unknown";
}

We deliberately sprinkled various non-access modifiers throughout our Person object, which each constructor annotation handles differently. We’ll use a copy of this class with a different name for each of the following sections.

3. @AllArgsConstructor

As the name suggests, the @AllArgsConstructor annotation generates a constructor initializing all object fields. Fields annotated with @NonNull undergo a null check in the resulting constructor.

Let’s add the annotation to our class:

@AllArgsConstructor
public class AllArgsPerson {
    // ...
}

Next, let’s trigger a null check in the generated constructor:

@Test
void whenUsingAllArgsConstructor_thenCheckNotNullFields() {
    assertThatThrownBy(() -> {
        new AllArgsPerson(10, "Asian", null);
    }).isInstanceOf(NullPointerException.class)
      .hasMessageContaining("name is marked non-null but is null");
}

The @AllArgsConstructor provided us with a AllArgsPerson constructor containing all the necessary fields of the object.

4. @RequiredArgsConstructor

The @RequiredArgsConstructor generates a constructor that initializes only fields marked as final or @NonNull, provided they weren’t initialized upon declaration.

Let’s update our class with the @RequiredArgsConstructor:

@RequiredArgsConstructor
public class RequiredArgsPerson {
    // ...
}

With our RequiredArgsPerson object, this results in a constructor with only two parameters:

@Test
void whenUsingRequiredArgsConstructor_thenInitializedFinalFieldsWillBeIgnored() {
    RequiredArgsPerson person = new RequiredArgsPerson("Hispanic", "Isabela");
    assertEquals("unknown", person.getNickname());
}

Since we initialized the nickname field, it won’t become part of the generated constructor arguments, despite being final. Instead, it’s treated like other non-final fields and those not marked as @NotNull.

Like @AllArgsConstructor, the @RequiredArgsConstructor annotation also conducts a null check for fields annotated with @NonNull, as demonstrated in our unit test:

@Test
void whenUsingRequiredArgsConstructor_thenCheckNotNullFields() {
    assertThatThrownBy(() -> {
        new RequiredArgsPerson("Hispanic", null);
    }).isInstanceOf(NullPointerException.class)
      .hasMessageContaining("name is marked non-null but is null");
}

When using @AllArgsConstructor or @RequiredArgsConstructor, it’s crucial to maintain the object field order. For instance, if we swapped the name and race fields in our Person object, it wouldn’t trigger compiler complaints due to their identical types. However, existing users of our library might overlook the need to adjust constructor parameters.

5. @NoArgsConstructor

Normally, if we haven’t defined a constructor, Java provides a default one. Likewise, @NoArgsConstructor generates a no-argument constructor for a class, resembling the default constructor. We specify the force parameter flag to avoid compilation errors caused by uninitialized final fields:

@NoArgsConstructor(force = true)
public class NoArgsPerson {
    // ...
}

Next, let’s check for defaults on an uninitialized field:

@Test
void whenUsingNoArgsConstructor_thenAddDefaultValuesToUnInitializedFinalFields() {
    NoArgsPerson person = new NoArgsPerson();
    assertNull(person.getRace());
    assertEquals("unknown", person.getNickname());
}

Unlike the other fields, the nickname field didn’t receive a null default value because we initialized it upon declaration.

6. Using Multiple Annotations

In certain cases, differing requirements may lead to the use of multiple annotations. For example, if we prefer to offer a static factory method but still need a default constructor for compatibility with external frameworks such as JPA, we can use two annotations:

@RequiredArgsConstructor(staticName = "construct")
@NoArgsConstructor(access = AccessLevel.PRIVATE, force = true)
public class SpecialPerson {
    // ...
}

Thereafter, let’s call our static constructor with sample values:

@Test 
void whenUsingRequiredArgsConstructorWithStaticName_thenHideTheConstructor() { 
    SpecialPerson person = SpecialPerson.construct("value1", "value2"); 
    assertNotNull(person); 
}

In this scenario, attempting to instantiate the default constructor results in a compilation error.

7. Comparison Summary

Let’s summarize in a table what we’ve discussed:

Annotation Generated constructor arguments @NonNull field null check
@AllArgsConstructor All object fields (except for static and initialized final fields) Yes
@RequiredArgsConstructor Only final or @NonNull fields Yes
@NoArgsConstructor None No

8. Conclusion

In this article, we explored the constructor annotations offered by Project Lombok. We learned that @AllArgsConstructor initializes all object fields, whereas @RequiredArgsConstructor initializes only final and @NotNull fields. Additionally, we discovered that @NoArgsConstructor generates a default-like constructor, and we discussed how these annotations can be used together.

As always, the source code for all the examples can be found over on GitHub.

Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE
res – REST with Spring (eBook) (everywhere)
Subscribe
Notify of
guest
2 Comments
Oldest
Newest
Inline Feedbacks
View all comments