This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

preface

Whether you are a white programmer, a CRUD BOY with three or five years of experience, or a senior Java engineer, we often encounter nullPointerExceptions in our daily development because of null. In order to avoid this exception, We often need to make some non-null judgments about the objects we use, and we always start with if… Else, such as the following code:

Person person = getPersonById("123");
if(person ! =null) {
    Address add = person.getAddress();
    if(add ! =null) {
        String city = add.getCity();
        if(city ! =null) { System.out.println(city); }}}Copy the code

This if approach makes code less readable and maintainable, and it’s easy to forget, leading to bugs.

Java 8 has introduced Optional

to address this issue. Let’s take a look at the official documentation first.

A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value.

Additional methods that depend on the presence or absence of a contained value are provided, such as orElse() (return a default value if value not present) and ifPresent() (execute a block of code if the value is present).

This is a value-based class; use of identity-sensitive operations (including reference equality (==), identity hash code, or synchronization) on instances of Optional may have unpredictable results and should be avoided.

Optional

is a container object that holds a value that may or may not be null and provides a set of methods that depend on whether the value is null. Optional

is a value-based class, so operations like ==, hash, or synchronization should be avoided because these operations are based on the spatial address of the object rather than the value.

methods

Let’s take a look at what methods Optional

provides.

Create the Optional

Optional.empty()

Returns an empty Optional instance.

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

Optional.of(T value)

Returns Optional with the current non-null value of Optional

public static <T> Optional<T> of(T value) {
    return new Optional<>(value);
}
private Optional(T value) {
    this.value = Objects.requireNonNull(value);
}
Copy the code

The object passed in by this method must not be null; if it is null, a null-pointer exception is thrown.

Optional.ofNullable(T value)

Returns an Optional specified value, or an empty Optional if not empty.

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

The only difference between ofNullable(T value) and of(T value) is that you can pass in an empty object, which equals empty().

Other related methods

isPresent

Returns true if a value exists, false otherwise.

public boolean isPresent(a) {
returnvalue ! =null;
}
Copy the code

ifPresent

If a value exists, the specified consumer is invoked with that value, otherwise nothing is done.

public void ifPresent(Consumer<? super T> consumer) {
if(value ! =null)
consumer.accept(value);
}
Copy the code

Note the distinction between isPresent() and isPresent().

orElse

If value has a return value, otherwise return Other.

public T orElse(T other) {
    returnvalue ! =null ? value : other;
}
Copy the code

orElseGet

Return value (if present), otherwise call other and return the result of that call.

public T orElseGet(Supplier<? extends T> other) {
    returnvalue ! =null ? value : other.get();
}
Copy the code

orElseThrow

Returns the included value, if present, otherwise throws the exception created by the supplied exceptionSupplier.

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
    if(value ! =null) {
        return value;
    } else {
        throwexceptionSupplier.get(); }}Copy the code

map

Convert the original Optional

to Optional
. If the value is null, empty Optional is returned.

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
    Objects.requireNonNull(mapper);
    if(! isPresent())return empty();
    else {
        The map method wraps the result of apply as Optional
        returnOptional.ofNullable(mapper.apply(value)); }}Copy the code

If person is empty, option.ofnullable (person.getAddress()) is returned:

Optional<Person> pOptional = Optional.ofNullable(getPersonById(123));
Optional<Address> address = pOptional.map(new Function<Person, Address>() {
    @Override
    public Address apply(Person person) {
        // Returns a value
        returnperson.getAddress(); }});Copy the code

The above code can be simplified as:

Optional<Address> add = pOptional.map(Person::getAddress);
Copy the code

flatMap

Mapper () = mapper (); mapper () = mapper (); map() = mapper (); The apply() method on the mapper parameter of flatMap() returns an Optional object.

public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
    Objects.requireNonNull(mapper);
    if(! isPresent())return empty();
    else {
        returnObjects.requireNonNull(mapper.apply(value)); }}Copy the code

The examples in map() above are implemented using flatMap() as follows:

Optional<Address> address1 = pOptional.flatMap(new Function<Person, Optional<Address>>() {
    @Override
    public Optional<Address> apply(Person person) {
        // Wrap yourself as Optional
        returnOptional.of(person.getAddress()); }});Copy the code

It can also be abbreviated as:

pOptional.flatMap(person1 -> Optional.of(person1.getAddress()));
Copy the code

filter

Filter the value, returning Optional itself if it exists and returns true in predicate, or empty() otherwise.

public Optional<T> filter(Predicate<? super T> predicate) {
    Objects.requireNonNull(predicate);
    if(! isPresent())return this;
    else
        return predicate.test(value) ? this : empty();
}
Copy the code

Such as the following cases:

Optional<Person> pOptional = Optional.ofNullable(getPersonById(123));
Optional<String> nameOpt = pOptional.filter(new Predicate<Person>() {
    @Override
    public boolean test(Person person) {
        return person.getAge() > 10;
    }
}).map(Person::getName);
System.out.println(nameOpt.orElse("Unknown"));
Copy the code

Abbreviated to:

Optional<String> nameOpt = pOptional.filter(p -> p.getAge() > 10).map(Person::getName);
System.out.println(nameOpt.orElse("Unknown"));
Copy the code

So do you want to immediately put your if… What about the else? Review our original code:

Person person = getPersonById("123");
if(person ! =null) {
    Address add = person.getAddress();
    if(add ! =null) {
        String city = add.getCity();
        if(city ! =null) { System.out.println(city); }}}Copy the code

Let’s look at it with Optional:

Person person = getPersonById("123");
Optional<String> cityOption = Optional.ofNullable(person)
        .map(Person::getAddress)
        .map(Address::getCity);
System.out.println("city is :"+cityOption.orElse("Unknown city."));
Copy the code

Does it feel like the code looks “high and mighty,” but is it the right one? There are a lot of Optional objects created in this process, which is at least more space wasteful.

What is the correct use of Optional?

Optional use principle

Brian Goetz, the architect of the Java language, clearly defined:

Optional is intended to provide a limited mechanism for library method return types where there needed to be a clear way to represent “no result,” and using null for such was overwhelmingly likely to cause errors.

Optional aims to provide a limited mechanism for library method return types;

There needs to be an explicit way to say “no results”;

Using NULL in this case would most likely result in an error.

Optional is intended to be used as a method return value, explicitly indicating that the method does not return a result.

So with that intent in mind, let’s look at some of the principles that we need to follow when we use Optional in terms of intent.

Don’t overuse Optional

Sometimes when we learn something, we just want to use it right away. For example, when we have a hammer, everything looks like a nail.

I always wanted to knock. For example:

Avoid:

public String fetchStatus(a) {
    String status = ... ;
    return Optional.ofNullable(status).orElse("PENDING");
}
Copy the code

And should be:

public String fetchStatus(a) {
    String status = ... ;
    return status == null ? "PENDING" : status;
}
Copy the code

Using Optional not only makes it less readable, but it also builds an Optional object.

Do not declare any domain objects Optional

Do not set any method arguments (especially setters) and constructor arguments to Optional.

Since Optional cannot be serialized, Optional is not designed to be used as an object property.

If you want to use Optional to ensure that the object property is not set to null, you can do the following:

// PREFER
public class Customer {
    private final String name;     // Cannot be null
    private final String postcode; // Can be null
    public Cart(String name, String postcode) {
        this.name = Objects.requireNonNull(name, () -> "Name cannot be null");
        this.postcode = postcode;
    }

    public Optional<String> getPostcode(a) {
        returnOptional.ofNullable(postcode); }... }Copy the code

You can see here that the getPostcode() method returns an Optional option, but don’t change all getters in your code to that, especially if you’re returning collections or arrays, just return empty collections and arrays, because collections themselves have empty() methods to determine if they’re empty.

Using Optionalin in method parameters is another common mistake. This leads to higher code complexity. Instead of forcing the caller to use Optional, you should check the parameters inside the method.

Do not replace the collection return value with Optional

The representation method returns no results by returning an empty collection, and builds the empty returns with collections.emptyList (), emptyMap(), and emptySet().

Compare Optional values using equals

Since the Optional class is a value-based class, its equals method itself compares values, so if you need to compare two Optional values, you can use the equals method directly.

public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }
    if(! (objinstanceof Optional)) {
        return false; } Optional<? > other = (Optional<? >) obj;return Objects.equals(value, other.value);
}
Copy the code

Do not assign a Null value to an Optional variable

Option.empty () initializes an Optional value instead of null. Optional is a container and null is meaningless.

Avoid the following code:

public Optional<Cart> fetchCart(a) {
    Optional<Cart> emptyCart = null; . }Copy the code

And should be:

public Optional<Cart> fetchCart(a) { Optional<Cart> emptyCart = Optional.empty(); . }Copy the code

Make sure Optional has a value before calling get()

If you need to call the get() method to get the Optional value, be sure to check the Optional value before calling it; IsPresent ()-get() is usually used, but this is not elegant; But don’t forget isPresent() if you must choose to use get().

Avoid the following code:

Optional<Cart> cart = ... ; // May return an empty Optional./ / if the cart is empty throws Java. Util. NoSuchElementException
Cart myCart = cart.get();
Copy the code

And should be:

if(cart.isPresent()) { Cart myCart = cart.get(); . }else{... }Copy the code

When the value is null, the default object is returned through the orElse() method

This is more elegant than isPresent()-get(). However, there is a slight problem with the orElse() method being executed even if Optional has a value, and an object needs to be built, so there is a slight loss in performance.

Avoid the following code:

public static final String USER_STATUS = "UNKNOWN"; .public String findUserStatus(long id) {

    Optional<String> status = ... ; // May return an empty Optional

    if (status.isPresent()) {
        return status.get();
    } else {
        returnUSER_STATUS; }}Copy the code

And should be:

public static final String USER_STATUS = "UNKNOWN";

public String findUserStatus(long id) {

    Optional<String> status = ... ; // May return an empty Optional

    return status.orElse(USER_STATUS);
}
Copy the code

If the value is null, the default object is returned through the orElseGet() method

OrElseGet () is an elegant alternative to isPresent()-get(). It is also more efficient than orElse() because the orElseGet() method takes the Supplier from Java8 and only when the Optional value is empty, The Supplier’s get() method is executed, with no performance penalty compared to ‘orElse().

Avoid the following code:

public String computeStatus(a) {... }public String findUserStatus(long id) {

    Optional<String> status = ... ;

    if (status.isPresent()) {
        return status.get();
    } else {
        returncomputeStatus(); }}Copy the code

Also avoid:

public String computeStatus(a) {... }public String findUserStatus(long id) {
    Optional<String> status = ... ; 
    // computeStatus() is also called when status is not empty
    return status.orElse(computeStatus()); 
}
Copy the code

And should be:

public String computeStatus(a) {...// some code used to compute status
}

public String findUserStatus(long id) {
    Optional<String> status = ... ; 
    // computeStatus() is only called when status is empty
    return status.orElseGet(this::computeStatus);
}
Copy the code

conclusion

Optional may seem simple at first glance, but it’s not easy to use correctly. The OptionalAPI is mainly used for return types, and clearly expresses the possibility that there is no result in the return value.

  • Don’t overuseOptional
  • OptionalTry to use it as the return value of a method
  • Check if there is a value before getting it
  • Appropriate use ofOptional API

Improper use can lead to bugs in some cases, so bookmark this article for Optional use.

Draw that

  • This event is officially supported by the Nuggets for details of the Diggnation program.

  • Please feel free to discuss in the comments section. The nuggets will draw 100 nuggets in the comments section after the diggnation project. See the event article for details.

  • Comments, suggestions, discussions related to the content of the article, and general comments such as “treading” and “learning” will not win prizes

The discussion topics are ready for everyone. Think about the problems of these people in the picture below and whether there are bugs in the code submitted at last. You can discuss them in the comment area.