Return Optional not null

Returning a possibly-null object from a method is usually undesirable. The caller of such a method may not be aware that it can return null. If the caller has not written code to handle the null case, and a null is returned, then an error will almost always result.

The problem with returning null is that the caller is not forced into handling the null case.

In these common cases:

you can usually simply return an empty object instead of null, and it will work fine, without special handling. In these cases, there's usually no need for the caller to explicitly handle the empty case.

What do you do when a suitable, well-behaved "empty" object is not available? In those cases, you should return an Optional<T>. Optional<T> was created especially for nullable return values. When the return type of a method is Optional<T>, then the caller is forced into explicitly handling the null case.

Example

In the example below, the class has a private LocalDate that holds a birth date. It has no "empty" default value. The getBirthDate method returns an Optional<LocalDate> to the caller.

import java.time.LocalDate;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;

public final class Human {

  /** 
   Constructor. 
    
   @param name never null or empty
   @param address never null, can be empty
   @param friends never null, can be empty; a defensive copy is made 
   @param birthDate can be null!
  */
  public Human(String name, String address, Set<Human> friends, LocalDate birthDate){
    this.name = Objects.requireNonNull(name).trim();
    if (this.name.length() == 0){
      throw new IllegalArgumentException("Name is empty.");
    }
    this.address = Objects.requireNonNull(address).trim(); 
    this.friends.addAll(Objects.requireNonNull(friends)); //defensive copy
    this.birthDate = birthDate; //immutable, no need to copy
  }
  
  //..elided
  
  /** Never null or empty. */
  public String getName() {
    return name;
  }
  
  /** Might be an empty String. */
  public String getAddress() {
    return address;
  }

  /** Might be an empty Set. */
  public Set<Human> getFriends() {
    //make sure the caller can't modify the private Set
    return Collections.unmodifiableSet(friends);
  }
  
  /** 
   May or may not be present. 
   
   The returned Optional object is itself never null! 
   That would completely defeat the purpose of using {@code Optional<T>}. 
  */
  public Optional<LocalDate> getBirthDate() {
    return Optional.ofNullable(birthDate);
  }
  
  /** Debugging only. */
  @Override public String toString(){
    String SEP = ", ";
    return name + SEP + address + SEP + friends + SEP + birthDate; 
  }

  // PRIVATE
  
  /** Required. Never empty. Its value will come from somewhere. */
  private String name;
  
  /** Optional. Default to an empty String. */
  private String address = "";

  /** Optional. Default to an empty Set. */
  private Set<Human> friends = new LinkedHashSet<>();
  
  /** 
   Optional. No suitable default! Possibly null.
   
   Note that the field itself doesn't have to be of type {@code Optional<LocalDate>}.
   Optional is not Serializable; it can't be passed over the 
   network, or persisted to a file system. 
  */
  private LocalDate birthDate;
  
  /** 
   Exercise this class.
   
   Output:
   <pre>
    Jane, 123 Easy Street, [], 1966-06-30
    Bob, 456 Happy Path, [Jane, 123 Easy Street, [], 1966-06-30], null
    Birthdate unknown.
    1966-06-30
   </pre>
  */
  public static void main(String... args) {
    Human jane = new Human(
      "Jane", "123 Easy Street", Collections.emptySet(), LocalDate.of(1966, 6, 30)
    );
    
    Set<Human> friends = new LinkedHashSet<>();
    friends.add(jane);
    Human bob = new Human("Bob", "456 Happy Path", friends, null);
    
    log(jane.toString(), bob.toString());
    
    /**
     This kind of code is OK, but you should really try and 
     use one of the helper methods defined by Optional. 
    */
    if (bob.getBirthDate().isPresent()){
      log(bob.getBirthDate());
    }
    else {
      log("Birthdate unknown.");
    }
   
    //here's an example of using one of Optional's helper methods
    jane.getBirthDate().ifPresent(birthDate -> log(birthDate));
    bob.getBirthDate().ifPresent(birthDate -> log(birthDate)); //nothing output!
  }
  
  private static void log(Object... msgs){
    Stream.of(msgs).forEach(System.out::println);
  }
}
 

See Also :
Prefer empty items to null ones
Avoid null if possible