eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

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

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

Partner – LambdaTest – NPI EA (cat= Testing)
announcement - icon

Distributed systems often come with complex challenges such as service-to-service communication, state management, asynchronous messaging, security, and more.

Dapr (Distributed Application Runtime) provides a set of APIs and building blocks to address these challenges, abstracting away infrastructure so we can focus on business logic.

In this tutorial, we'll focus on Dapr's pub/sub API for message brokering. Using its Spring Boot integration, we'll simplify the creation of a loosely coupled, portable, and easily testable pub/sub messaging system:

>> Flexible Pub/Sub Messaging With Spring Boot and Dapr

1. Overview

Generally, null variables, references and collections are tricky to handle in Java code. They are not only hard to identify but also complex to deal with.

As a matter of fact, any miss in dealing with null cannot be identified at compile time and results in a NullPointerException at runtime.

In this tutorial, we’ll take a look at the need to check for null in Java and various alternatives that help us to avoid null checks in our code.

Further reading:

Using NullAway to Avoid NullPointerExceptions

Learn how to avoid NullPointerExceptions using NullAway.

Spring Null-Safety Annotations

A quick and practical guide to null-safety annotations in Spring.

Introduction to the Null Object Pattern

Learn about the Null Object Pattern and how to implement it in Java

2. What Is NullPointerException?

According to the Javadoc for NullPointerException, it’s thrown when an application attempts to use null in a case where an object is required, such as:

  • Calling an instance method of a null object
  • Accessing or modifying a field of a null object
  • Taking the length of null as if it were an array
  • Accessing or modifying the slots of null as if it were an array
  • Throwing null as if it were a Throwable value

Let’s quickly see a few examples of the Java code that cause this exception:

public void doSomething() {
    String result = doSomethingElse();
    if (result.equalsIgnoreCase("Success")) 
        // success
    }
}

private String doSomethingElse() {
    return null;
}

Here, we tried to invoke a method call for a null reference. This would result in a NullPointerException.

Another common example is if we try to access a null array:

public static void main(String[] args) {
    findMax(null);
}

private static void findMax(int[] arr) {
    int max = arr[0];
    //check other elements in loop
}

This causes a NullPointerException at line 6.

So, accessing any field, method, or index of a null object causes a NullPointerException, as can be seen from the examples above.

A common way of avoiding the NullPointerException is to check for null:

public void doSomething() {
    String result = doSomethingElse();
    if (result != null && result.equalsIgnoreCase("Success")) {
        // success
    }
    else
        // failure
}

private String doSomethingElse() {
    return null;
}

In the real world, programmers find it hard to identify which objects can be null. An aggressively safe strategy could be to check null for every object. However, this causes a lot of redundant null checks and makes our code less readable.

In the next few sections, we’ll go through some of the alternatives in Java that avoid such redundancy.

3. Handling null Through the API Contract

As discussed in the last section, accessing methods or variables of null objects causes a NullPointerException. We also discussed that putting a null check on an object before accessing it eliminates the possibility of NullPointerException.

However, there are often APIs that can handle null values:

public void print(Object param) {
    System.out.println("Printing " + param);
}

public Object process() throws Exception {
    Object result = doSomething();
    if (result == null) {
        throw new Exception("Processing fail. Got a null response");
    } else {
        return result;
    }
}

The print() method call would just print “null” but won’t throw an exception. Similarly, process() would never return null in its response. It rather throws an Exception.

So, for a client code accessing the above APIs, there is no need for a null check.

However, such APIs need to make it explicit in their contract. A common place for APIs to publish such a contract is the Javadoc.

But this gives no clear indication of the API contract and thus relies on the client code developers to ensure its compliance.

In the next section, we’ll see how a few IDEs and other development tools help developers with this.

4. Automating API Contracts

4.1. Using Static Code Analysis

Static code analysis tools help improve the code quality a great deal. And a few such tools also allow the developers to maintain the null contract. One example is FindBugs.

FindBugs helps manage the null contract through the @Nullable and @NonNull annotations. We can use these annotations over any method, field, local variable, or parameter. This makes it explicit to the client code whether the annotated type can be null or not.

Let’s see an example:

public void accept(@NonNull Object param) {
    System.out.println(param.toString());
}

Here, @NonNull makes it clear that the argument cannot be null. If the client code calls this method without checking the argument for null, FindBugs would generate a warning at compile time.

4.2. Using IDE Support

Developers generally rely on IDEs for writing Java code. And features such as smart code completion and useful warnings, for example when a variable may not have been assigned, certainly help a lot.

Some IDEs also allow developers to manage API contracts and thereby eliminate the need for a static code analysis tool. IntelliJ IDEA provides the @NonNull and @Nullable annotations.

To add the support for these annotations in IntelliJ, we need to add the following Maven dependency:

<dependency>
    <groupId>org.jetbrains</groupId>
    <artifactId>annotations</artifactId>
    <version>24.1.0</version>
</dependency>

Now IntelliJ will generate a warning if the null check is missing, as in our last example.

IntelliJ also provides a Contract annotation for handling complex API contracts.

5. Assertions

Until now, we’ve only talked about removing the need for null checks from the client code. But that is rarely applicable in real-world applications.

Now let’s suppose that we’re working with an API that cannot accept null parameters or can return a null response that has to be handled by the client. This presents the need for us to check the parameters or the response for a null value.

Here, we can use Java Assertions instead of the traditional null check conditional statement:

public void accept(Object param){
    assert param != null;
    doSomething(param);
}

In line 2, we check for a null parameter. If the assertions are enabled, this would result in an AssertionError.

Although it is a good way of asserting preconditions such as non-null parameters, this approach has two major problems:

  1. Assertions are usually disabled in a JVM.
  2. A false assertion results in an unchecked error that is irrecoverable.

Therefore, it is not recommended for programmers to use Assertions for checking runtime conditions. In the following sections, we’ll discuss other ways of handling null validations.

6. Avoiding Null Checks Through Coding Practices

6.1. Preconditions

It’s usually a good practice to write code that fails early. So, if an API accepts multiple parameters that aren’t allowed to be null, it’s better to check for every non-null parameter as a precondition of the API.

Let’s look at two methods — one that fails early and one that doesn’t:

public void goodAccept(String one, String two, String three) {
    if (one == null || two == null || three == null) {
        throw new IllegalArgumentException();
    }

    process(one);
    process(two);
    process(three);
}

public void badAccept(String one, String two, String three) {
    if (one == null) {
        throw new IllegalArgumentException();
    } else {
        process(one);
    }

    if (two == null) {
        throw new IllegalArgumentException();
    } else {
        process(two);
    }

    if (three == null) {
        throw new IllegalArgumentException();
    } else {
        process(three);
    }
}

Clearly, we should prefer goodAccept() over badAccept().

As an alternative, we can also use Guava’s Preconditions for validating API parameters.

6.2. Using Primitives Instead of Wrapper Classes

Since null is not an acceptable value for primitives like int, we should prefer them over their wrapper counterparts like Integer wherever possible.

Consider two implementations of a method that sums two integers:

public static int primitiveSum(int a, int b) {
    return a + b;
}

public static Integer wrapperSum(Integer a, Integer b) {
    return a + b;
}

Now let’s call these APIs in our client code:

int sum = primitiveSum(null, 2);

This would result in a compile-time error since null is not a valid value for an int.

And when using the API with wrapper classes, we get a NullPointerException:

assertThrows(NullPointerException.class, () -> wrapperSum(null, 2));

There are also other factors for using primitives over wrappers, as we covered in another tutorial, Java Primitives Versus Objects.

6.3. Empty Collections

Occasionally, we need to return a collection as a response from a method. For such methods, we should always try to return an empty collection instead of null:

public List<String> names() {
    if (userExists()) {
        return Stream.of(readName()).collect(Collectors.toList());
    } else {
        return Collections.emptyList();
    }
}

This way, we’ve avoided the need for our client to perform a null check when calling this method.

7. Using Objects

Java 7 introduced the new Objects API. This API has several static utility methods that take away a lot of redundant code.

Let’s look at one such method, requireNonNull():

public void accept(Object param) {
    Objects.requireNonNull(param);
    // doSomething()
}

Now let’s test the accept() method:

assertThrows(NullPointerException.class, () -> accept(null));

So, if null is passed as an argument, accept() throws a NullPointerException.

This class also has isNull() and nonNull() methods that can be used as predicates to check an object for null.

8. Using Optional

8.1. Using orElseThrow

Java 8 introduced a new Optional API in the language. This offers a better contract for handling optional values compared to null.

Let’s see how Optional takes away the need for null checks:

public Optional<Object> process(boolean processed) {
    String response = doSomething(processed);

    if (response == null) {
        return Optional.empty();
    }

    return Optional.of(response);
}

private String doSomething(boolean processed) {
    if (processed) {
        return "passed";
    } else {
        return null;
    }
}

By returning an Optional, as shown above, the process method makes it clear to the caller that the response can be empty and needs to be handled at compile time.

This notably takes away the need for any null checks in the client code. An empty response can be handled differently using the declarative style of the Optional API:

assertThrows(Exception.class, () -> process(false).orElseThrow(() -> new Exception()));

Furthermore, it also provides a better contract to API developers to signify to the clients that an API can return an empty response.

Although we eliminated the need for a null check on the caller of this API, we used it to return an empty response.

To avoid this, Optional provides an ofNullable method that returns an Optional with the specified value, or empty, if the value is null:

public Optional<Object> process(boolean processed) {
    String response = doSomething(processed);
    return Optional.ofNullable(response);
}

8.2. Using Optional With Collections

While dealing with empty collections, Optional comes in handy:

public String findFirst() {
    return getList().stream()
      .findFirst()
      .orElse(DEFAULT_VALUE);
}

This function is supposed to return the first item of a list. The Stream API’s findFirst function will return an empty Optional when there is no data. Here, we have used orElse to provide a default value instead.

This allows us to handle either empty lists or lists that, after we have used the Stream library’s filter method, have no items to supply.

Alternatively, we can also allow the client to decide how to handle empty by returning Optional from this method:

public Optional<String> findOptionalFirst() {
    return getList().stream()
      .findFirst();
}

Therefore, if the result of getList is empty, this method will return an empty Optional to the client.

Using Optional with collections allows us to design APIs that are sure to return non-null values, thus avoiding explicit null checks on the client.

It’s important to note here that this implementation relies on getList not returning null. However, as we discussed in the last section, it’s often better to return an empty list rather than a null.

8.3. Combining Optionals

When we start making our functions return Optional, we need a way to combine their results into a single value.

Let’s take our getList example from earlier. What if it were to return an Optional list, or were to be wrapped with a method that wrapped a null with Optional using ofNullable?

Our findFirst method wants to return an Optional first element of an Optional list:

public Optional<String> optionalListFirst() {
   return getOptionalList()
      .flatMap(list -> list.stream().findFirst());
}

By using the flatMap function on the Optional returned from getOptional, we can unpack the result of an inner expression that returns Optional. Without flatMap, the result would be Optional<Optional<String>>. The flatMap operation is only performed when the Optional is not empty.

9. Libraries

9.1. Using Lombok

Lombok is a great library that reduces the amount of boilerplate code in our projects. It comes with a set of annotations that take the place of common parts of code we often write ourselves in Java applications, such as getters, setters and toString(), to name a few.

Another of its annotations is @NonNull. So, if a project already uses Lombok to eliminate boilerplate code, @NonNull can replace the need for null checks.

Before we move on to some examples, let’s add a Maven dependency for Lombok:

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

Now we can use @NonNull wherever a null check is needed:

public void accept(@NonNull Object param){
    System.out.println(param);
}

So, we simply annotated the object for which the null check would’ve been required, and Lombok generates the compiled class:

public void accept(@NonNull Object param) {
    if (param == null) {
        throw new NullPointerException("param");
    } else {
        System.out.println(param);
    }
}

If param is null, this method throws a NullPointerException. The method must make this explicit in its contract, and the client code must handle the exception.

9.2. Using StringUtils

Generally, String validation includes a check for an empty value in addition to null value.

Therefore, this would be a common validation statement:

public void accept(String param){
    if (null != param && !param.isEmpty())
        System.out.println(param);
}

This quickly becomes redundant if we have to deal with a lot of String types. This is where StringUtils comes in handy.

Before we see this in action, let’s add a Maven dependency for commons-lang3:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.12.0</version>
</dependency>

Let’s now refactor the above code with StringUtils:

public void accept(String param) {
    if (StringUtils.isNotEmpty(param))
        System.out.println(param);
}

So, we replaced our null or empty check with a static utility method isNotEmpty(). This API offers other powerful utility methods for handling common String functions.

10. Conclusion

In this article, we looked at the various reasons for NullPointerException and why it is hard to identify.

Then we saw various ways to avoid the redundancy in code around checking for null with parameters, return types and other variables.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

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

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

Course – LS – NPI (cat=Java)
announcement - icon

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

>> CHECK OUT THE COURSE

eBook Jackson – NPI EA – 3 (cat = Jackson)