How to use the Optional API for JDK8 series?

1, Optional brief introduction

In the previous chapter, we learned about lambada expressions, method references, functional interfaces, etc. In this blog, we continue to learn about Optional features in JDK8. Jdk8 designed this Optional option to avoid nullPointerExceptions that are common in development

Optional is a guaranteed step toward functional programming in Java and helps with implementation within the paradigm

2. Why does Optional avoid null Pointers?

Why does Optional avoid NullPointerExceptions? The following code can be found inside:

/**
     * 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);
    }

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

If value is null, return empty() as an Optional object. New Optional<>()

   /**
     * Common instance for {@code empty()}.
     */
    private static finalOptional<? > EMPTY =new Optional<>();
Copy the code

And the other method is option.of (),

 /**
  * 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);
  }
Copy the code

Next, we can see that if T value is passed null, the null pointer is still abnormal

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

3. Create an Optional instance

  • Create an empty Optional using the empty() method.
 // Create an empty Optional using the empty() method
Optional<User> empOpt = Optional.empty();
  empOpt.get();
Copy the code
  • Use the of() method to create an Optional value that contains the value. You cannot pass in a null value
User user = new User();
Optional<User> opt = Optional.of(user);
Copy the code
  • Use the ofNullable() method to create an Optional containing the value, passing in a null value
User userObj = null;
Optional<User> userOptional = Optional.ofNullable(userObj);
Copy the code

4. Access the value of the Optional object

Get the value of the Optional object using the get() method

String name = "jack";
Optional<String> strOpt = Optional.ofNullable(name);
String getStr = strOpt.get();
System.out.println(getStr);

Copy the code

Seems to be very normal, if passing a null values, will throw exceptions Java. Util. NoSuchElementException: No value the present, so you need to use ifPresent, avoid the userInfo value is null

 City city = new City("Shanghai");
Address address = new Address("200000",city);
  User userInfo = new User("jack"."15588899988" , "[email protected]", address);
  Optional.ofNullable(userInfo).ifPresent(us -> System.out.println(us.toString()));
Copy the code
  • IsPresent does not recommend this, and syntactically it is ok, but the designers intended to use the concise Java syntax of the Expression language
Optional<User> userOptiion = Optional.ofNullable(userInfo);
if (userOptiion.isPresent()) {
    User userInfomation = userOptiion.get();
}
Copy the code

5. Optional Returns the default value

  • Use orElse() to return the default, if there is a value, and default if there is no data
 // Use orElse() to return the default value, if there is a value, no data return the default value
User tUser = null;
 System.out.println("using orElse");
 tUser = Optional.ofNullable(tUser).orElse(defaultUserInfo());
 System.out.println("default user information:"+tUser.toString());
Copy the code
  • Return the default value using orElseGet(). This method returns a value if there is one, and if there is no value, it executes the Supplier functional interface passed in as an argument
 User teUser = null;
System.out.println("using orElseGet");
 teUser = Optional.ofNullable(teUser).orElseGet(() -> defaultUserInfo());
 System.out.println("default user information:"+teUser.toString());
Copy the code

DefaultUserInfo, returns default user data

 protected static User defaultUserInfo(a){
    System.out.println("create default user information!");
    City city = new City("Shanghai");
    Address address = new Address("200000",city);
    User userInfo = new User("jack"."15588899988" , "[email protected]", address);
    return userInfo;
}

Copy the code

At first glance, they both seem to work the same way, right? What’s the difference? Or to verify by example:

If the value passed in is null, compare orElse with orElseGet

User tUser = null;
System.out.println("using orElse");
tUser = Optional.ofNullable(tUser).orElse(defaultUserInfo());
System.out.println("default user information:"+tUser.toString());

User teUser = null;
System.out.println("using orElseGet");
teUser = Optional.ofNullable(teUser).orElseGet(() -> defaultUserInfo());
System.out.println("default user information:"+teUser.toString());
Copy the code

As you can see, both orElse and orElseGet call the default method

using orElse

create default user information! using orElseGet create default user information!

If the value passed in is not null, orElse calls the default method, and orElseGet does not call the default method

 User tUser = new User("jack"."15588899988" , "[email protected]", address);
System.out.println("using orElse");
 tUser = Optional.ofNullable(tUser).orElse(defaultUserInfo());
 System.out.println("default user information:"+tUser.toString());
      
User teUser = new User("jack"."15588899988" , "[email protected]", address);
System.out.println("using orElseGet");
teUser = Optional.ofNullable(teUser).orElseGet(() -> defaultUserInfo());
System.out.println("default user information:"+teUser.toString());
Copy the code

using orElse

create default user information! using orElseGet

OrElse and orElseGet call the default method if the value passed in is null. In the case that the value passed is not null, orElse calls the default method, and orElseGet does not call the default method

6. Optional returns an exception

Optional also defines the orElseThrow() API, which throws an exception when the object is empty rather than returning an alternate value

 User u = null;
 User userData = Optional.ofNullable(u).orElseThrow(() -> new IllegalArgumentException());
Copy the code

7, Optional conversion value

To convert data, you can use the map() method to convert Optional values

The map example:

User us = new User("tom"."15588899988" , "[email protected]".null."");
String email = Optional.ofNullable(us).map(userInfor -> userInfor.getEmail()).orElse("[email protected]");
Copy the code

If the data in the class is encapsulated with Optional, it looks like this:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;

import java.util.Optional;

@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {
  	// ...
    private String position;
    public Optional<String> getPosition(a) {
        returnOptional.ofNullable(position); }}Copy the code

When flatMap() is used as an argument, the value returned is the unwrapped String value

String position = Optional.ofNullable(us).flatMap(use -> use.getPosition()).orElse("default");
Copy the code

8, Optional filter value

In addition to transforming values, the Optional class also provides methods to “filter” values by conditions, and filter() accepts a Predicate argument

Optional<User> uoptional = Optional.ofNullable(us).filter(euser -> euser.getEmail()! =null && euser.getEmail().contains("@"));
System.out.println(uoptional.get().toString());
Copy the code

9. A typical Optional example

A typical example of a nested call:


@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {
    private String name;
    private String mobiTel;
    private String email;
    private Address address;
    private String position;
    public Optional<String> getPosition(a) {
        returnOptional.ofNullable(position); }}@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Address {

    private String emailCode;
    private City city;

}

@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class City {
    private String cityName;

}

Copy the code

Draw a diagram to show class relationships:Uml class diagram:

Jdk7 writing:

/**
 * the example of jdk7 getCityName .<br>
 * @Author  nicky.ma
 * @Date2021/07/20 my *@Param [user]
 * @return java.lang.String
 */
protected static String getCityName(User user) {
    if(user ! =null) {
        Address address = user.getAddress();
        if(address ! =null) {
            City city = address.getCity();
            if(city ! =null) {
                returncity.getCityName(); }}}throw new IllegalArgumentException("Wrong value");
}
Copy the code

Jdk8 writing:

/**
  * the example of jdk8 getCityName .<br>
  * @Author nicky.ma
  * @Date2021/07/20 out *@Param [user]
  * @return java.lang.String
  */
 protected static String obtainCityName(User user) {
     return Optional.ofNullable(user)
             .map(u -> u.getAddress())
             .map(a -> a.getCity())
             .map(c -> c.getCityName())
             .orElseThrow(() -> new IllegalArgumentException("Wrong value"));
 }

Copy the code

10. Appendix Reference materials

  • www.runoob.com/java/java8-…
  • Beginnersbook.com/2017/10/jav…
  • Howtodoinjava.com/java-8-tuto…