An overview of the

The Optional class addresses the infamous NullPointerException, provides methods to replace the if-else logic of the past, and provides consistent functional programming in conjunction with the Stream Stream.

Note: Optional does not support the serialization and deserialization mechanisms provided by Java itself, that is, RPC cannot use the serialization mechanisms provided by Java

The official introduction

/** * A container object which may or may not contain a non-null value. * If a value is present, {@code isPresent()} will return {@code true} and * {@code get()} will return the value. * * <p>Additional methods that depend on the presence or absence of a contained * value are provided, such as {@link #orElse(java.lang.Object) orElse()} * (return a default value if value not present) and * {@link #ifPresent(java.util.function.Consumer) ifPresent()} (execute a block * of code if the value is present). * * <p>This is  a <a href=".. /lang/doc-files/ValueBased.html">value-based</a> * class; use of identity-sensitive operations (including reference equality * ({@code ==}), identity hash code, or synchronization) on instances of * {@code Optional} may have unpredictable results and should be avoided. * * @since 1.8 A container object that may or may not have a non-null value. IsPresent () returns true if the value exists, isPresent() is treated as null if there is no value, isPresent() returns false; More methods depend on whether the container contains a value, such as orElse(returns a default value when there is no value) ifPresent(Consumer C) performs an action when the value is present; This is a value-based class that uses identity-sensitive operations, including comparing references to ==, hashcode, synchronization against an Optional object that may have unexpected results and should then be avoided. Note in writing the API: Optional was originally designed to be the return value of a method when it was explicitly required to represent the absence of a value. Return null, possible error; The Optional object returned is not a null object, it always points to an Optional object instance. * /Copy the code

Methods the overview

The source code


public final class Optional<T> {
    /**
     * Common instance for {@code empty()}.
     */
    private static final Optional<?> EMPTY = new Optional<>();

    /**
     * If non-null, the value; if null, indicates no value is present
     */
    private final T value;

    /**
     * Constructs an empty instance.
     *
     * @implNote Generally only one empty instance, {@link Optional#EMPTY},
     * should exist per VM.
     */
    private Optional() {
        this.value = null;
    }

    /**
     * Returns an empty {@code Optional} instance.  No value is present for this
     * Optional.
     *
     * @apiNote Though it may be tempting to do so, avoid testing if an object
     * is empty by comparing with {@code ==} against instances returned by
     * {@code Option.empty()}. There is no guarantee that it is a singleton.
     * Instead, use {@link #isPresent()}.
     *
     * @param <T> Type of the non-existent value
     * @return an empty {@code Optional}
     */
    public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }

    /**
     * Constructs an instance with the value present.
     *
     * @param value the non-null value to be present
     * @throws NullPointerException if value is null
     */
    private Optional(T value) {
        this.value = Objects.requireNonNull(value);
    }

    /**
     * Returns an {@code Optional} with the specified present non-null value.
     *
     * @param <T> the class of the value
     * @param value the value to be present, which must be non-null
     * @return an {@code Optional} with the value present
     * @throws NullPointerException if value is null
     */
    public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }

    /**
     * Returns an {@code Optional} describing the specified value, if non-null,
     * otherwise returns an empty {@code Optional}.
     *
     * @param <T> the class of the value
     * @param value the possibly-null value to describe
     * @return an {@code Optional} with a present value if the specified value
     * is non-null, otherwise an empty {@code Optional}
     */
    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }

    /**
     * If a value is present in this {@code Optional}, returns the value,
     * otherwise throws {@code NoSuchElementException}.
     *
     * @return the non-null value held by this {@code Optional}
     * @throws NoSuchElementException if there is no value present
     *
     * @see Optional#isPresent()
     */
    public T get() {
        if (value == null) {
            throw new NoSuchElementException("No value present");
        }
        return value;
    }

    /**
     * Return {@code true} if there is a value present, otherwise {@code false}.
     *
     * @return {@code true} if there is a value present, otherwise {@code false}
     */
    public boolean isPresent() {
        return value != null;
    }

    /**
     * If a value is present, invoke the specified consumer with the value,
     * otherwise do nothing.
     *
     * @param consumer block to be executed if a value is present
     * @throws NullPointerException if value is present and {@code consumer} is
     * null
     */
    public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }

    /**
     * If a value is present, and the value matches the given predicate,
     * return an {@code Optional} describing the value, otherwise return an
     * empty {@code Optional}.
     *
     * @param predicate a predicate to apply to the value, if present
     * @return an {@code Optional} describing the value of this {@code Optional}
     * if a value is present and the value matches the given predicate,
     * otherwise an empty {@code Optional}
     * @throws NullPointerException if the predicate is null
     */
    public Optional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
    }

    /**
     * If a value is present, apply the provided mapping function to it,
     * and if the result is non-null, return an {@code Optional} describing the
     * result.  Otherwise return an empty {@code Optional}.
     *
     * @apiNote This method supports post-processing on optional values, without
     * the need to explicitly check for a return status.  For example, the
     * following code traverses a stream of file names, selects one that has
     * not yet been processed, and then opens that file, returning an
     * {@code Optional<FileInputStream>}:
     *
     * <pre>{@code
     *     Optional<FileInputStream> fis =
     *         names.stream().filter(name -> !isProcessedYet(name))
     *                       .findFirst()
     *                       .map(name -> new FileInputStream(name));
     * }</pre>
     *
     * Here, {@code findFirst} returns an {@code Optional<String>}, and then
     * {@code map} returns an {@code Optional<FileInputStream>} for the desired
     * file if one exists.
     *
     * @param <U> The type of the result of the mapping function
     * @param mapper a mapping function to apply to the value, if present
     * @return an {@code Optional} describing the result of applying a mapping
     * function to the value of this {@code Optional}, if a value is present,
     * otherwise an empty {@code Optional}
     * @throws NullPointerException if the mapping function is null
     */
    public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Optional.ofNullable(mapper.apply(value));
        }
    }

    /**
     * If a value is present, apply the provided {@code Optional}-bearing
     * mapping function to it, return that result, otherwise return an empty
     * {@code Optional}.  This method is similar to {@link #map(Function)},
     * but the provided mapper is one whose result is already an {@code Optional},
     * and if invoked, {@code flatMap} does not wrap it with an additional
     * {@code Optional}.
     *
     * @param <U> The type parameter to the {@code Optional} returned by
     * @param mapper a mapping function to apply to the value, if present
     *           the mapping function
     * @return the result of applying an {@code Optional}-bearing mapping
     * function to the value of this {@code Optional}, if a value is present,
     * otherwise an empty {@code Optional}
     * @throws NullPointerException if the mapping function is null or returns
     * a null result
     */
    public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Objects.requireNonNull(mapper.apply(value));
        }
    }

    /**
     * Return the value if present, otherwise return {@code other}.
     *
     * @param other the value to be returned if there is no value present, may
     * be null
     * @return the value, if present, otherwise {@code other}
     */
    public T orElse(T other) {
        return value != null ? value : other;
    }

    /**
     * Return the value if present, otherwise invoke {@code other} and return
     * the result of that invocation.
     *
     * @param other a {@code Supplier} whose result is returned if no value
     * is present
     * @return the value if present otherwise the result of {@code other.get()}
     * @throws NullPointerException if value is not present and {@code other} is
     * null
     */
    public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }

    /**
     * Return the contained value, if present, otherwise throw an exception
     * to be created by the provided supplier.
     *
     * @apiNote A method reference to the exception constructor with an empty
     * argument list can be used as the supplier. For example,
     * {@code IllegalStateException::new}
     *
     * @param <X> Type of the exception to be thrown
     * @param exceptionSupplier The supplier which will return the exception to
     * be thrown
     * @return the present value
     * @throws X if there is no value present
     * @throws NullPointerException if no value is present and
     * {@code exceptionSupplier} is null
     */
    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }

    /**
     * Indicates whether some other object is "equal to" this Optional. The
     * other object is considered equal if:
     * <ul>
     * <li>it is also an {@code Optional} and;
     * <li>both instances have no value present or;
     * <li>the present values are "equal to" each other via {@code equals()}.
     * </ul>
     *
     * @param obj an object to be tested for equality
     * @return {code true} if the other object is "equal to" this object
     * otherwise {@code false}
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (!(obj instanceof Optional)) {
            return false;
        }

        Optional<?> other = (Optional<?>) obj;
        return Objects.equals(value, other.value);
    }

    /**
     * Returns the hash code value of the present value, if any, or 0 (zero) if
     * no value is present.
     *
     * @return hash code value of the present value or 0 if no value is present
     */
    @Override
    public int hashCode() {
        return Objects.hashCode(value);
    }

    /**
     * Returns a non-empty string representation of this Optional suitable for
     * debugging. The exact presentation format is unspecified and may vary
     * between implementations and versions.
     *
     * @implSpec If a value is present the result must include its string
     * representation in the result. Empty and present Optionals must be
     * unambiguously differentiable.
     *
     * @return the string representation of this instance
     */
    @Override
    public String toString() {
        return value != null
            ? String.format("Optional[%s]", value)
            : "Optional.empty";
    }
}
Copy the code

Methods to introduce

1. Optional

1 / / / / constructor to obtain an empty container empty () @ Test (expected = NoSuchElementException. Class) public void whenCreateEmptyOptional_thenNull () {  Optional<User> emptyOpt = Optional.empty(); emptyOpt.get(); // No value will throw an exception}Copy the code
2 / / / / constructor needs to check the NPE use of () method in advance @ Test (expected = NullPointerException. Class) public void test_ofNullable () {String name = "John"; Optional<Object> opt = Optional.ofNullable(name); }Copy the code
@test public void test_ofNullable() {String name = "John"; Optional<Object> opt = Optional.ofNullable(name); }Copy the code

Value of 2.

//get() @Test(expected = NoSuchElementException.class) public void whenCreateEmptyOptional_thenNull() { Optional<User> emptyOpt = Optional.ofNullable(null); emptyOpt.get(); // No value will throw an exception}Copy the code
@test public void test_orElse() {String name = "1"; String name1 = "John"; String s = Optional.ofNullable(name).orElse(name1); System.out.println(s); }Copy the code
//orElseGet() supports functional programming // This method returns a value if it has one. If there is no value, it executes the Supplier function interface passed in as an argument. @test public void test_orElseGet() {User u1 = new User(" 三", 11); @test public void test_orElseGet() {User u1 = new User(" 三", 11); User u2 = null; User s = Optional.ofNullable(u2).orElseGet(() -> u1); System.out.println(s); }Copy the code

How orElse() differs from orElseGet()

@Test public void givenEmptyValue_whenCompare_thenOk() { User user = null; System.out.println("Using orElse"); User result = Optional.ofNullable(user).orElse(createNewUser()); System.out.println("Using orElseGet"); User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser()); } private User createNewUser() { System.out.println("Creating New User"); return new User("[email protected]", 123); } Execution result: Using orElse Creating New User Using orElseGet Creating New User @Test public void givenPresentValue_whenCompare_thenOk() { User user = new User("[email protected]", 123); System.out.println("Using orElse"); User result = Optional.ofNullable(user).orElse(createNewUser()); System.out.println("Using orElseGet"); User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser()); } Using orElse Creating New User Using orElseGetCopy the code

Abnormal returns

    @Test(expected = IllegalArgumentException.class)
    public void whenThrowException_thenOk() {
        User user = null;
        User result = Optional.ofNullable(user)
                .orElseThrow( () -> new IllegalArgumentException());
    }
Copy the code

Check processing

    isPresent()
    @Test
    public void test_isPresent(){
        Optional<String> s = Optional.ofNullable(null);
        System.out.println(s.isPresent());
    }
Copy the code
IfPresent () // In addition to performing the check, the method also accepts a Consumer argument. If the object is not empty, @test public void test_ifPresent(){String s1 = "q"; Optional<String> s = Optional.ofNullable(s1); s.ifPresent(value -> System.out.println(value = value.toUpperCase())); System.out.println(s.get()); }Copy the code

Conversion value

@test public void test_map() {User User = new User(); Optional<User> s = Optional.of(user).map((x) -> { x.getName().equals("zhangsn"); return x; }); System.out.println(s.get()); }Copy the code
    //flatMap()
    @Test
    public void test_flatMap() {
        User user = new User();
        Address address = Optional.ofNullable(user).flatMap(x -> x.getAddress()).orElse(new Address());
        System.out.println(address);
    }
Copy the code

filter

In addition to converting values, the Optional class also provides a way to "filter" values by criteria. // Filter () takes a Predicate argument and returns the value of true as a result of the test. If the test result is false, an empty Optional is returned. User user = new User("[email protected]", 123); user = null; Optional<User> result = Optional.ofNullable(user) .filter(u -> u.getName() ! = null && u.getName().contains("@")); System.out.println(result.get());Copy the code

Best practices

There is a temptation to call get() to get the value. We all know the getters/setters for normal Javabeans. And expect that if we call get… () we’ll get something. When you call the getter for a normal bean, you never get any thrown exceptions. However, if the call calls the get method on Optional and the option is empty inside, NoSuchElementException is thrown.

These methods should be called getOrThorwSomeHorribleError (), so the first and second rule:

1. Do not assign NULL to Optional

2, Avoid Optional. Get (). Never call get() if you can’t prove that an option exists.

Use orElse(), orElseGet(), orElseThrow(). Get your results.

You can refactor the code

    String variable = fetchSomeVaraible();
    if(variable == null){
     1. throw new IllegalStateException("No such variable");
     2. return createVariable();
     3. return "new variable";
    } else { 
     ... 
     100 lines of code
     ...
    }
Copy the code

Refactoring to

    1. 
    Optional<String> variableOpt = fetchOptionalSomeVaraible();
    String variable = variableOpt.orElseThrow(() -> new Exeption("")) 
    ... 100 lines of code ...
    2.
    Optional<String> variableOpt = fetchOptionalSomeVaraible();
    String variable = variableOpt.orElseGet(() -> createVariable()) 
    ... 100 lines of code ...
    3.
    Optional<String> variableOpt = fetchOptionalSomeVaraible();
    String variable = variableOpt.orElse("new variable") 
    ... 100 lines of code ...
Copy the code

Note, orElse (..) Is eager to compute, which means the following code:

    Optional<Dog> optionalDog = fetchOptionalDog();
    optionalDog
     .map(this::printUserAndReturnUser)
     .orElse(this::printVoidAndReturnUser)
Copy the code

Both methods are executed if the value is present, and only the last method is executed if the value is not. To handle these cases, we can use the orElseGet () method, which takes supplier as an argument and is lazy.

3. Do not use Optional in fields, method parameters, or collections.

We assign thatField directly to the class field:

Public void setThatField (Optional <ThatFieldType> thatField) {this.thatfield = thatField; }Copy the code

Instead of

SetThatField (Optional. OfNullable (thatField));Copy the code

4. Use Optional as the return type only when the result is uncertain.

Honestly, this is the only good place to use Optional.

Our goal is to provide a limited mechanism for the return types of library methods, where there needs to be an explicit way to say “no result,” and the use of NULL for such methods can definitely lead to errors.

5. Don’t be afraid to use Maps and Filters.

There are some general development practices worth following called SLA-P: Single Layer of first uppercase Abstraction letters.

Here is the code to be refactored:

Dog dog = fetchSomeVaraible(); String dogString = dogToString(dog); public String dogToString(Dog dog){ if(dog == null){ return "DOG'd name is : " + dog.getName(); } else { return "CAT"; }}Copy the code

Refactoring:

    Optional<Dog> dog = fetchDogIfExists();
    String dogsName = dog
     .map(this::convertToDog)
     .orElseGet(this::convertToCat)
    public void convertToDog(Dog dog){
       return "DOG'd name is : " + dog.getName();
    }
    public void convertToCat(){
       return "CAT";
    }
Copy the code

Filter is a useful folding syntax:

Dog dog = fetchDog(); if(optionalDog ! = null && optionalDog.isBigDog()){ doBlaBlaBla(optionalDog); }Copy the code

The above code can be refactored to:

    Optional<Dog> optionalDog = fetchOptionalDog();
    optionalDog
     .filter(Dog::isBigDog)
     .ifPresent(this::doBlaBlaBla)
Copy the code

6. Do not use optional for chained methods.

One thing to be aware of when using optional is the temptation of the chained method. Things can look pretty when we link methods like the builder pattern :). But it doesn’t always equal more readability. So don’t do this:

  Optional
   .ofNullable(someVariable)
   .ifPresent(this::blablabla)
Copy the code

It’s not good for performance, and it’s not good for readability. We should avoid null references whenever possible.

7. Make all expressions into single-line lambdas

This is the more general rule that I think should apply to flows as well. But this article is about Optional. The important thing to remember with Optional is that the left side of the equation is just as important as the right:

Optional .ofNullable(someVariable) .map(variable -> { try{ return someREpozitory.findById(variable.getIdOfOtherObject()); } catch (IOException e){ LOGGER.error(e); throw new RuntimeException(e); }}) .filter(variable -> { if(variable.getSomeField1() ! = null){ return true; } else if(variable.getSomeField2() ! = null){ return false; } else { return true; } }) .map((variable -> { try{ return jsonMapper.toJson(variable); } catch (IOException e){ LOGGER.error(e); throw new RuntimeException(e); }})) .map(String::trim) .orElseThrow(() -> new RuntimeException("something went horribly wrong."))Copy the code

The above verbose code block can be replaced with a method:

    Optional
     .ofNullable(someVariable)
     .map(this::findOtherObject)
     .filter(this::isThisOtherObjectStale)
     .map(this::convertToJson)
     .map(String::trim)
     .orElseThrow(() -> new RuntimeException("something went horribly wrong."));
Copy the code

other

Java 9 adds three methods for the Optional class: OR (), ifPresentOrElse(), and stream().

  • The or method

The or method returns the Optional value if it exists, or a default value otherwise.

  • The stream method

The stream method converts the Optional to a stream. If the Optional contains a value, the stream containing the value is returned. Otherwise, an empty stream is returned.

  • IfPresentOrElse method

In java8, the ifPresent method is used instead of the traditional if(user! = null) if (user! = null) else {// XXX}; the Optional ifPresent method in Java8 does not support else operations. In java9, the ifPresentOrElse method is equivalent to if (user! = null) else {// XXX} operation.

Reference documentation

Overview of Java Optional usage practices

An overview of best practices for using Java Optional

Understand, learn, and use Optional in Java