Explain Codes LogoExplain Codes Logo

Should a retrieval method return null or throw an exception when it can't produce the return value?

java
null-checks
error-handling
optional
Nikita BarsukovbyNikita Barsukov·Aug 9, 2024
TLDR

Your approach should depend on expected outcomes: return null when the value might be absent; throw an exception when its absence denotes an error. In simpler terms:

Find (Optional fetch for a user) = null is cool.

public User findUserById(String id) { return userRepo.getById(id); // Returns null if there's no user. Tough luck! }

Get (Mandatory fetch for a user) = Yell out (exception) if the user is missing!

public User getUserById(String id) { User user = userRepo.getById(id); if (user == null) { throw new IllegalArgumentException("No user found. The ID " + id + " seems made up...nice try!"); } return user; }

Your approach needs to be predictable and consistent all across your code. Documentation can be your best friend!

Dealing with null

Returning null makes sense when the absence of a value is, well, normal. This typically happens in:

  • Lookup operations (a user might not exist in your system).
  • Indicating that there are no operations to be performed.
  • Scenarios where your program can handle null without going kaput.

In scenarios where null is a possible response, consider using Optional class to amplify clearer intentions:

public Optional<User> findUserByEmail(String email) { return Optional.ofNullable(userRepo.findByEmail(email)); // Say no to NPE surprises! }

Exceptions: not always exceptional!

An exception is thrown when the absence of a return value signals an error. They match scenarios like:

  • Guaranteed results (like fetching system configuration data).
  • Processing that depends on a successful retrieval (like processing a payment).
  • Critical business processes where missing entities mean big trouble!

Keeping it consistent

By maintaining consistency you can prevent headaches down the road:

  • Establish error handling policies at the project or team level.
  • Document and use clear naming conventions.
  • Helper methods can abstract null checks and exception throwings. Less code, more fun!

Intuition for the rescue

Avoid misleading or ambiguous values. Instead:

  • Use exceptions to provide a detailed breakdown of what went wrong.
  • Use return values that don't make developers guess what they mean.

Domino effect: implications on existing code

Consider the side effects on the calling code:

  • Having to include null checks everywhere can lead to messy and hard-to-read codes.
  • Exceptions can make control flow hard to predict and may demand additional handling logic claims.
  • The chosen approach should minimize the "surprise element" for the developers.

The lost art of expressing intentions in code

Code should be as expressive as a book. Well-known patterns like Null Object pattern or Factory Method pattern can be helpful to make your coding intentions explicitly known.

Specific use cases = specific strategies

Always analyze the specific use case:

  • Is the absence of a value hinting at a problem?
  • Can the method's contract tolerate a null value?
  • Do you want to force the calling code to deal with a possible absence?

Dealing with complexity

As your app grows and gets complicated, your error handling strategies can have profound effects:

  • The performance impact of creating exceptions should be considered for high load scenarios.
  • Predictable error handling strategies make for easier testing and debugging.
  • Security matters! Make sure exceptions don't leak sensitive info.