Backend Development 15 min read

Handling Null Values and Optional in Java Backend Development

This article discusses common null‑value pitfalls in Java services, compares returning null collections versus empty collections, introduces the Null‑Object pattern and JDK 8/Guava Optional, and provides practical guidelines using JSR‑303/JSR‑305 annotations to make APIs safer and more expressive.

Selected Java Interview Questions
Selected Java Interview Questions
Selected Java Interview Questions
Handling Null Values and Optional in Java Backend Development

Introduction

In many Java projects developers encounter scattered null‑value checks that make the code hard to understand and can lead to NullPointerExceptions. This article summarizes several techniques for handling nulls safely.

Null Values in Business Logic

Scenario

Consider a UserSearchService interface with two methods:

public interface UserSearchService {
  List
listUser();
  User get(Integer id);
}

Problem Spot

When testing the interface with TDD we notice two questions:

Should listUser() return an empty list or null when there is no data?

Should get(Integer id) throw an exception or return null when the user does not exist?

Deep Dive into listUser

A typical implementation returns null for an empty result:

public List
listUser() {
  List
userList = userListRepository.selectByExample(new UserExample());
  if (CollectionUtils.isEmpty(userList)) {
    return null; // spring util
  }
  return userList;
}

Returning null forces callers to perform null checks and can cause NullPointerExceptions. A safer version returns an empty list:

public List
listUser() {
  List
userList = userListRepository.selectByExample(new UserExample());
  if (CollectionUtils.isEmpty(userList)) {
    return Lists.newArrayList(); // guava
  }
  return userList;
}

Now the method always returns a List , even when there are no elements.

Deep Dive into get

A naive implementation simply returns the result of a repository call, which may be null :

public User get(Integer id) {
  return userRepository.selectByPrimaryKey(id); // may be null
}

To make the contract explicit, add Javadoc with an @exception tag:

/**
 * Get user information by ID.
 * @param id user ID
 * @return user entity
 * @exception UserNotFoundException if the user does not exist
 */
User get(Integer id);

Alternatively, express the possible absence with Optional :

public interface UserSearchService {
  /**
   * Get user information by ID.
   * @param id user ID
   * @return Optional containing the user or empty if not found
   */
  Optional
getOptional(Integer id);
}

Implementation:

public Optional
getOptional(Integer id) {
  return Optional.ofNullable(userRepository.selectByPrimaryKey(id));
}

Parameter Constraints

Whether the id parameter is mandatory can be enforced with annotations:

Strong constraint using JSR‑303: @NotNull Integer id

Documentation constraint using JSR‑305: @CheckForNull or @Nonnull

public interface UserSearchService {
  /** Get user by ID */
  User get(@NotNull Integer id);

  /** Get user optionally */
  Optional
getOptional(@NotNull Integer id);
}

Null‑Object Pattern

When converting a domain object to a DTO, a null source object often leads to many field‑by‑field null checks. Define a special subclass that returns default values:

static class NullPerson extends Person {
  @Override public String getAge() { return ""; }
  @Override public String getName() { return ""; }
}

Then the conversion becomes straightforward:

Person person = getPerson(); // may return NullPerson
personDTO.setDtoAge(person.getAge());
personDTO.setDtoName(person.getName());

Using Optional can achieve the same without a concrete subclass:

Optional.ofNullable(getPerson()).ifPresent(p -> {
  personDTO.setDtoAge(p.getAge());
  personDTO.setDtoName(p.getName());
});

Optional as Return Value

Returning Optional clearly signals that the user may be absent:

public interface UserService {
  Optional
get(Integer id);
}

For collections, prefer returning an empty list rather than Optional > to avoid double‑wrapping ambiguity.

Optional as Parameter – Not Recommended

Using Optional for method arguments creates unclear semantics (e.g., does an empty Optional mean “all users” or “no filter”?). Split the method into two overloads instead:

public interface UserService {
  List
listUser(String username);
  List
listUser(); // returns all users
}

When a parameter can be null, document it with JSR‑303/JSR‑305 annotations instead of wrapping it in Optional .

Guidelines Summary

Return empty collections instead of null unless there is a compelling reason.

Use Optional for single‑value returns where absence is a normal case.

Never use Optional as a method parameter.

Avoid overusing Optional in bean getters; it pollutes code.

BackendDesign PatternsJavaOptionalJSR303Null HandlingJSR305
Selected Java Interview Questions
Written by

Selected Java Interview Questions

A professional Java tech channel sharing common knowledge to help developers fill gaps. Follow us!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.