Optional - HolmesJJ/OOP-FP GitHub Wiki

Definition

  • Simplify the judgment processing of null value in Java to prevent NPE (NullPointerException).
  • Optional encapsulates an object and it contains an attribute value, which is the value of this object.
/* Optional source code */
public final class Optional<T> {

    private final T value; // the value of the encapsulated object
    ...
}

Brief Introdction

public class Person {

    private String name;
    private int score;

    Person(String name, int score) {
        this.name = name;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public int getScore() {
        return score;
    }
}

/** 
 * This is a method to get the person's name. 
 * To prevent NullPointerException, must check whether the Person object 
 * is null before returning the name.
 */

/* Before Java 8 */
public static String getName(Person person) {
    if (person == null) {
        return "unknown";
    }
    return person.getName();
}

/* From Java 8 */
public static String getName(Person person) {
    return Optional.ofNullable(person).map(p -> p.getName()).orElse("unknown");
}

/**
 * Bad case (DON'T DO THIS)
 * This case not only does not remove the null check process, but also add the Optional encapsulation process,
 * which violates the original intention of Optional.
 */
public static String getName(Person person) {
    Optional<Person> personOpt = Optional.ofNullable(person);
    if (personOpt.isPresent()) {
        return personOpt.get().getName();
    }
    return "unknown";
}

Detail Introdction

Create Optional

/* Optional source code */
public final class Optional<T> {

    private static final Optional<?> EMPTY = new Optional<>();

    private final T value;

    private Optional() {
        this.value = null;
    }

    private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }

    public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }

    public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }

    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }
    ...
}

From the Optional source code:

  • The two constructor methods are private, so it is impossible to create Optional objects with new Optional().
  • The Optional class provides three static methods empty(), of(T value), ofNullable(T value) to create an Optional object.
  1. Create an empty Optional object
Optional optional = Optional.empty();
  1. Create an Optional object with a non-null value
  • The value must be non-null, otherwise NullPointerException will be thrown.
Optional optional = Optional.of("optional"); 
  1. Create an Optional object with a nullable value
  • The value can be null. If the value is null, the returned result is the same as Optional.empty().
Optional optional = Optional.ofNullable(null);

Use of methods in the Optional

get()

  • The get() method is used to return the value of the encapsulated object. If the value of the encapsulated object is null, NoSuchElementException will be thrown. Therefore, the returned value does not need to determine whether it is null as long as no exception is thrown.
/* Optional source code */
public final class Optional<T> {
    public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }
    ...
}

isPresent()

  • Determine whether the value of the encapsulated object is null. not null -> true; null -> false
/* Optional source code */
public final class Optional<T> {
    public boolean isPresent() {
        return value != null;
    }
    ...
}

isEmpty()

  • Determine whether the value of the encapsulated object is null. null -> true; not null -> false
/* Optional source code */
public final class Optional<T> {
    public boolean isEmpty() {
        return value == null;
    }
    ...

ifPresent()

  • Accept a Consumer object, if the value of the encapsulated object is not null, then execute the accept() method of the Consumer object.
/* Optional source code */
public final class Optional<T> {
    public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
   }
   ...
}

Example

public static void printName(Person person) {
    Optional.ofNullable(person).ifPresent(p -> System.out.println("The person's name is : " + p.getName()));
}

ifPresentOrElse()

  • Accept a Consumer object, if the value of the encapsulated object is not null, then execute the accept() method of the Consumer object; else then execute the Runnable object.
/* Optional source code */
public final class Optional<T> {
    public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) {
        if (value != null) {
            action.accept(value);
        } else {
            emptyAction.run();
        }
    }
    ...
}

Example

public static void printName(Person person) {
    Optional.ofNullable(person).ifPresentOrElse(
            p -> System.out.println("The person's name is : " + p.getName()), 
            () -> System.out.println("unknown"));
}

filter()

  • Accept a Predicate object to filter the Optional object. If it meets the conditions of the Predicate, return the Optional object itself, otherwise return an empty Optional object.
/* Optional source code */
public final class Optional<T> {
    public Optional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent()) {
            return this;
        } else {
            return predicate.test(value) ? this : empty();
        }
    }
    ...
}

Example

public static void filterScore(Person person) {
    Optional.ofNullable(person).filter(p -> p.getScore() >= 50).ifPresentOrElse(
            p -> System.out.println(person.getName() + " passed"),
            () -> System.out.println("The person failed"));
}

map()

  • Accept a Function object to perform operations on the value of the encapsulated object, and encapsulated the result into a new Optional object.
/* Optional source code */
public final class Optional<T> {
    public <U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        // If the value of the encapsulated object is null
        if (!isPresent()) {
            return empty();
        } 
        // If the value of the encapsulated object is not null
        else {
            // ofNullable() will return empty() if the result of mapper.apply(value) is null
            return Optional.ofNullable(mapper.apply(value));
        }
    }
    ...
}

Example

// The type of p.getName() is String, so the type of Optional<String> is also String.
public static Optional<String> getName(Person person) {
    return Optional.ofNullable(person).map(p -> p.getName());
}

flatMap()

  • Accept a Function object to perform operations on the value of the encapsulated object, and return the operated result.
/* Optional source code */
public final class Optional<T> {
    public <U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper) {
        Objects.requireNonNull(mapper);
        // If the value of the encapsulated object is null
        if (!isPresent()) {
            return empty();
        }
        // If the value of the encapsulated object is not null
        else {
            @SuppressWarnings("unchecked")
            // throws NullPointerException if the result of mapper.apply(value) is null
            Optional<U> r = (Optional<U>) mapper.apply(value);
            return Objects.requireNonNull(r);
        }
    }
    ...
}

Example

public static Optional<String> getName(Person person) {
    return Optional.ofNullable(person).flatMap(p -> Optional.of(p.getName()));
}
System.out.println(getName(new Person("Oliver", 40)));
System.out.println(getName(new Person(null, 40)));

/* Output */
Oliver
Exception in thread "main" java.lang.NullPointerException

map() VS flatMap()

Method Function Input Function Output Return
map() ? super T ? extends U Optional<U>
flatMap() ? super T ? extends Optional<? extends U> Optional<U>
⚠️ **GitHub.com Fallback** ⚠️