An overview of the

This article introduces an Util — Optional that was introduced after JDK 1.8.

This article will explain the principle, usage and the way of giving practical examples.

introduce

In the development process, Null Point Exception is always a pain in the programmer’s heart, accidentally did not do (object! = null), the system will throw a null point Exception.

Even The inventor of Null, Tony Hall, called it Null: “I call it My Billion Dollar Mistake.”

So to hide the uncertainty of Null, Optional comes in.

What is Optional

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 been successful results and should be avoided. Since: 1.8

  1. The above is the introduction to Optional source code. He introduces Optional from the following aspects.
  2. Optional is a Container that stores non-null values that may or may not be values. In short, a value that can be null.
  3. The other methods provided depend on whether the container contains a value, such as isPresent() and orElse().
  4. A value-based class is a value-based class that does reference operations. It does not serialize, hash, or alter the value of the container after initialization. Or face the consequences!
  5. Available starting with 1.8.

Scan the source code

In fact, when we read the source code, we can see the following:

  1. Optional has no external constructors, and all constructors are private.

  2. Provide methods to initialize.

    1. Option.empty () builds an empty Optional
    2. Option. ofNullable(T value) Passes in a value, which can be Null, to build an Optional
    3. Option. of(T value) passes a value, not Null, and builds an Optional
  3. Provides a series of methods to get values.

    1. Get () gets the value stored in Optional, if Null throws “NoSuchElementException”
    2. OrElse (T other) gets the value stored in Optional. If Null, the object passed in as the parameter is returned
    3. orElseGet(Supplier
      other) gets a value stored in Optional. Null returns the structure of the function execution passed in
    4. orElseThrow(Supplier
      exceptionSupplier) gets a value stored in Optional. Null throws the result of the execution of the passed Exception function
  4. Provide inspection interface

    1. Boolean isPresent() checks whether the stored value is Null, false if it is Null, and true otherwise
    2. public void ifPresent(Consumer
      consumer) checks if the stored value is Null, and if not, performs the passed functional operation
  5. Provides a series of streaming operation interfaces

    1. Optional flatMap(Function
      mapper) has the same logic as the map in #b, but returns data wrapped in an Optional wrapper; As in the Map example, he returns the Optional name
    2. Optional map(Function
      mapper) extends U> mapper) extends U> mapper) extends U> mapper) extends U> mapper) extends U> mapper) extends U> mapper) extends U> mapper) extends U> mapper
    3. Optional filter(Predicate
      predicate) according to the functional operation selection Optional value, it can pass in the filter (state – > state. RunningThread. Equals (thread)) the operation of this type

These are all Optional methods. If you are interested, you can go to see the source code. Encapsulation is very good, and it is worth learning. Each method has a corresponding explanation, detailed notes, worth our study.

The actual operation

The basic sample

Let’s start with an example that we often use at work.

Example 1: Query a group of PO data from the database, filter the data, convert it to a DTO, and send the data to the front end.

The code could look like this:

if(CollectionUtils.isEmpty(userPOList)){ return new ArrayList<>(); ConvertorList <UserDTO> userDTOList = userconvertor.userpo2dto (userPOList); return userDTOList;Copy the code

As you can see, I use the collectionutils.isempty () method to determine whether the returned Collection isEmpty or null. If it is “empty”, I return an empty List in advance, which is handled by the upper caller.

This is what happens if you do it Optional.

List<UserPO> userPOList = UserDAO.selectList(userQuery);

List<UserDTO> userDTOList = new ArrayList<>();
 Optional.ofNullable(userPOList)
     .orElse(new ArrayList<>())
     .forEach(a -> userDTOList.add(userPo2Dto(a)));
Copy the code

In the above code, userdao.selectList still returns a List. After I put the data into the Stream, I used orElse method to fetch the data and directly used Stream forEach operation.

The DAO layer is then returned directly to Optional, and the first line of code is omitted.

On-line actual code

private Set<Integer> getRoleIdsByUserId(int tenantId, int userId) {
  return userRoleQuery.getInfoByUserId(tenantId, userId)
      .orElse(new ArrayList<>())
      .stream()
      .map(UserRoleRelationDO::getRoleId)
      .collect(Collectors.toSet());
}
Copy the code

The above code is written in the actual development process. Of course, this code does not use Optional Map and Filter functions, but only orElse and Stream operations. But you can do an example.

Optional<CustomerPO> customerPoOptional = customerQueryService.getCustomerById(getTenantId(), customerId); CustomerPO CustomerPO = customerPoOptional. OrElseThrow (() - > ExceptionFactory. BizException (" not query to the user information!" ));Copy the code

To show how orElseThrow() is used, throw an Exception if the value is null.

Advanced instructions

In some of the above simple uses, we can find that the two checking interfaces isPresent() and ifPresent() are not used, and both use orElseGet() and orElse() instead of get() directly. Why is that?

One is that the get() method will throw an error if it stores null data, so it is always called when it is certain that data exists.

So there’s a rule:

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

In addition, isPresent() and ifPresent() are generally used to check whether there is still data after making a series of decisions. The usual use is to obtain data stream operation.

Here’s an example (from GitHub) :

@Override
  public boolean isExecutedBy(Thread thread) {
    return Optional.ofNullable(runningState.get())
      .filter(state -> state.runningThread.equals(thread))
      .isPresent();
  }
Copy the code

Use advice

Finally, several usage criteria are given:

Do not use Optional in fields, method arguments, or collections.

That is, do not pass an input parameter, or a field, as Optional. Optional is used when the result is uncertain, and when used in an input parameter, it means that the caller does not know whether the data is null, which is ridiculous.

More importantly, define a return as Optional and give it to the caller to use directly.