Original SpringBoot practice: how to use Optional

Posted by wanted420 on Wed, 08 Sep 2021 11:41:30 +0200

Optional is also one of the new features of Java 8. It is used to handle the null pointer problem and can be used gracefully in the return and processing of the application query interface Impl. The official definition is:

The Optional class is a null able container object. If the value exists, the isPresent() method will return true, and calling the get() method will return the object.

Optional is a container: it can hold values of type T or just null. Optional provides many useful methods so that we don't need to explicitly detect null values.

The introduction of Optional class solves the null pointer exception very well.


Classic usage

orElse() and orElseGet()

Classic usage

When querying the database to get the return value, it may get null or normal null value due to network or error. Assuming that there is a remote REST call, this exception handling mechanism is more needed. When Impl processes the returned data, it can be divided into two kinds of logic to construct the Either object. The generic types are Optional objects Left and Right. If null or empty, it will be stuffed into Left, As an exception reminder, if it is not empty, carry out logical processing and insert Right;

package org.example.model;

import java.util.Optional;

public class Either<L,R> {
    public Either(Optional<L> left, Optional<R> right) {
        this.left = left;
        this.right = right;

    final public Optional<L> left;
    final public Optional<R> right;

    public static <L,R> Either<L,R> Left(L value){return  new Either<>(Optional.of(value),Optional.<R>empty());}
    public static <L,R> Either<L,R> Right(R value){return  new Either<>(Optional.<L>empty(),Optional.of(value));}


You can define Either in the impl interface, and then return different responddata in the controller for whether the left and right values of Either exist. The simulated Impi is as follows. A layer of completable future is packaged for asynchronous operation;

// Suppose you need to query the database
    public CompletableFuture<Either<ApiError,List<Student>>>  getStudents(){
        Student a = new Student(1, "sat", "zhangsan", "1");
        Student b = new Student(2, "hhh", "lisi", "2");
        Student c = new Student(3, "kg", "wangwu", "3");
        Student d = new Student(4, "ss", "mazi", "4");
        Student e = new Student(5, "tt", "zhaowu", "5");
        Student f = new Student(6, "dfa", "xuliu", "6");
        Student g = new Student(7, "safg", "dongqi", "7");
        Student h = new Student(8, "tsa", "chenba", "8");
        List<Student> studentList = new ArrayList<Student>();
            return completedFuture(Either.Left(ApiError.from("the list is empty or null.","xxx001")));
            return completedFuture(Either.Right(studentList));

In the controller, the processing value of impi is processed again, because it needs to return the response body of the front end. ApiError and ResponseData objects need to be defined:

package org.example.model;

public class ApiError {
    private static String errorname;
    private static String ID;

    public String getErrorname() {
        return errorname;

    public String getID() {
        return ID;

    public ApiError(String errorname, String ID) {
        this.errorname = errorname;
        this.ID = ID;

    public static ApiError from(String errorname, String ID) {
        return new ApiError(errorname,ID);

The ResponseData object is a unified response body:

package org.example.model;

public class ResponseData {
    public ResponseData() {

    public ResponseData(String reponseinfo, Object data) {

    public static String getReponseinfo() {
        return reponseinfo;

    public static Object data;
    public static String reponseinfo;
    public static ApiError apiError;

    public static ResponseData from(ApiError apiError){
        return new ResponseData(apiError.getID(),apiError.getErrorname());

    public static ResponseData success(Object data){
        return new ResponseData("success",data);

  In the controller layer, use optional to judge whether Left and Right have values, and wrap both exceptions and null values;

    public static CompletableFuture<ResponseData> test2(){
        return teacherService2.getStudents().thenComposeAsync(students->
                students.left.map(l-> completedFuture(ResponseData.from(l)))
            List<Student> studentList=students.right.get();
                return  completedFuture(ResponseData.success(studentList));
            }else {
                return completedFuture(ResponseData.success(Collections.EMPTY_LIST));


orElse() and orElseGet()

Optional.orElse - called whether the value exists or not;

Optional.orElseGet - returns the value if it exists, otherwise returns the default value

Optional.get - get the value. The value needs to exist. In the above example, after judging that Left does not exist, directly get Right.get();

Optional.isPresent - judge whether the value exists;

Optional.ofNullable - allow null parameters to be passed;

package org.example.model;
import java.util.Optional;

public class orElseTest {
    public static void main(String[] args){
        String xxxxx = null;
        String op1 = Optional.ofNullable(xxxxx).orElse("ye");
        String op2= Optional.ofNullable(xxxxx).orElseGet(() -> "zong");
        String op3 = Optional.ofNullable("111").orElse("gang");
        String op4 = Optional.ofNullable("222").orElseGet(() -> "hao");



Topics: Java Spring Boot optional