Java 8 Optional Best Guide

Reprinted public number: Silent Wang Er

It ’s never too late to learn, especially for the good things in Java 8, Optional is one of them. This class provides a class-level solution for representing optional values ​​instead of null references. As a Java programmer, I am really sick of NullPointerException (NPE), although I am familiar with it like an old friend, knowing that it is also necessary-the program is using an object but found that the value of this object is null , So the Java Virtual Machine threw it out in anger and used it as a scapegoat.

Of course, our programmers are responsible and will not sit back and watch, so there are a lot of null value checks. Although such inspections are sometimes unnecessary, we are used to routine matters. Finally, Java 8 can't stand it anymore, so Optional was introduced so that the code we write is no longer so rigid.

Java 8 Optional Best Guide

 

01, what will be the problem without Optional

Let's simulate an actual application scenario. Xiao Wang went to work on the first day and led Lao Ma to arrange a task for him, asking him to pull a member's name from the database based on the member ID, and then print the name to the console. Although it is new, but this task cannot fail Xiao Wang, so he wrote this code in 10 minutes:

public class WithoutOptionalDemo {
    class Member {
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

    public static void main(String[] args) {
        Member mem = getMemberByIdFromDB();
        if (mem != null) {
            System.out.println(mem.getName());
        }
    }

    public static Member getMemberByIdFromDB() {
        // 当前 ID 的会员不存在
        return null;
    }
}

Since the member with the current ID does not exist, the getMemberByIdFromDB () method returns null as the result of not obtaining the member, which means that when printing the member's name, you must first judge mem empty, otherwise it will throw an NPE exception ,Do not believe? Let Xiaowang try to remove if (mem! = Null), the console immediately prints the error stack to give you a look.

Exception in thread "main" java.lang.NullPointerException
    at com.cmower.dzone.optional.WithoutOptionalDemo.main(WithoutOptionalDemo.java:24)

02. How does Optional solve this problem

After Xiao Wang submitted the code, he happily went to Lao Ma for a new task. With an open mind, Xiao Wang asked Lao Ma to take a look at his code, so Lao Wang told him that he should try Optional to avoid unnecessary null value checking. Now, let's see how Xiao Wang solves the above problems through Optional.

public class OptionalDemo {
    public static void main(String[] args) {
        Optional<Member> optional = getMemberByIdFromDB();
        optional.ifPresent(mem -> {
            System.out.println("会员姓名是:" + mem.getName());
        });
    }

    public static Optional<Member> getMemberByIdFromDB() {
        boolean hasName = true;
        if (hasName) {
            return Optional.of(new Member("沉默王二"));
        }
        return Optional.empty();
    }
}
class Member {
    private String name;

    public String getName() {
        return name;
    }

    // getter / setter
}

The getMemberByIdFromDB () method returns Optional <Member> as the result, which means that Member may or may not exist. At this time, you can use the Lambda expression in the ifPresent () method of Optional to directly print the result.

The reason why Optional can solve the problem of NPE is because it clearly tells us that we don't need to judge it empty. It's like a road sign at a crossroad, clearly telling you where to go.

03, create Optional object

1) You can use the static method empty () to create an empty Optional object

Optional<String> empty = Optional.empty();
System.out.println(empty); // 输出:Optional.empty

2) You can use the static method of () to create a non-empty Optional object

Optional<String> opt = Optional.of("沉默王二");
System.out.println(opt); // 输出:Optional[沉默王二]

Of course, the parameter passed to the of () method must be non-null, that is to say, it cannot be null, otherwise NullPointerException will still be thrown.

String name = null;
Optional<String> optnull = Optional.of(name);

3) You can use the static method ofNullable () to create an Optional object that is both empty and non-nullable

String name = null;
Optional<String> optOrNull = Optional.ofNullable(name);
System.out.println(optOrNull); // 输出:Optional.empty

The ofNullable () method has a ternary expression inside. If the parameter is null, it returns the private constant EMPTY; otherwise, a new Optional object is created using the new keyword-no more NPE exceptions will be thrown.

04. Determine whether the value exists

The method isPresent () can be used to determine whether an Optional object exists. If it exists, the method returns true, otherwise it returns false-instead of obj! = Null.

Optional<String> opt = Optional.of("沉默王二");
System.out.println(opt.isPresent()); // 输出:true

Optional<String> optOrNull = Optional.ofNullable(null);
System.out.println(opt.isPresent()); // 输出:false

After Java 11, you can also judge the opposite result of isPresent () by the method isEmpty ().

Optional<String> opt = Optional.of("沉默王二");
System.out.println(opt.isPresent()); // 输出:false

Optional<String> optOrNull = Optional.ofNullable(null);
System.out.println(opt.isPresent()); // 输出:true

05, non-empty expression

The Optional class has a very modern method-ifPresent (), which allows us to execute some code using functional programming, so I call it a non-empty expression. If there is no such method, we usually need to judge the Optional object by isPresent () method and then execute the corresponding code:

Optional<String> optOrNull = Optional.ofNullable(null);
if (optOrNull.isPresent()) {
    System.out.println(optOrNull.get().length());
}

With ifPresent (), the situation is completely different, you can directly pass the Lambda expression to the method, the code is more concise and more intuitive.

Optional<String> opt = Optional.of("沉默王二");
opt.ifPresent(str -> System.out.println(str.length()));

After Java 9, you can also execute two results through the method ifPresentOrElse (action, emptyAction), execute action when not empty, and execute emptyAction when empty.

Optional<String> opt = Optional.of("沉默王二");
opt.ifPresentOrElse(str -> System.out.println(str.length()), () -> System.out.println("为空"));

06. Set (get) the default value

Sometimes, when we create (get) an Optional object, we need a default value, and the orElse () and orElseGet () methods come in handy.

The orElse () method is used to return the value wrapped in the Optional object. If the value is not null, it returns; otherwise, it returns the default value. The parameter type of this method is consistent with the value type.

String nullName = null;
String name = Optional.ofNullable(nullName).orElse("沉默王二");
System.out.println(name); // 输出:沉默王二

The orElseGet () method is similar to the orElse () method, but with different parameter types. If the value in the Optional object is null, the function in the parameter is executed.

String nullName = null;
String name = Optional.ofNullable(nullName).orElseGet(()->"沉默王二");
System.out.println(name); // 输出:沉默王二

Judging from the output results and the form of the code, these two methods are very similar. This inevitably raises our doubts. Is it necessary for the designers of the Java class library to do this?

Suppose now that there is such a method for obtaining default values, a very traditional way.

public static String getDefaultValue() {
    System.out.println("getDefaultValue");
    return "沉默王二";
}

Then, call the getDefaultValue () method through the orElse () method and orElseGet () method respectively to return the default value.

public static void main(String[] args) {
    String name = null;
    System.out.println("orElse");
    String name2 = Optional.ofNullable(name).orElse(getDefaultValue());

    System.out.println("orElseGet");
    String name3 = Optional.ofNullable(name).orElseGet(OrElseOptionalDemo::getDefaultValue);
}

Note: Class name :: The method name is a syntax introduced in Java 8. There is no () after the method name, indicating that the method is not necessarily called.

The output is as follows:

orElse
getDefaultValue

orElseGet
getDefaultValue

The output is similar, not much different, this is when the value of the Optional object is null. What if the value of the Optional object is not null?

public static void main(String[] args) {
    String name = "沉默王三";
    System.out.println("orElse");
    String name2 = Optional.ofNullable(name).orElse(getDefaultValue());

    System.out.println("orElseGet");
    String name3 = Optional.ofNullable(name).orElseGet(OrElseOptionalDemo::getDefaultValue);
}

The output is as follows:

orElse
getDefaultValue
orElseGet

Hey, orElseGet () didn't call getDefaultValue (). Which method has better performance, do you understand?

07, get the value

Intuitively, semantically, the get () method is the most authentic method for obtaining the value of the Optional object, but unfortunately, this method is flawed, because if the value of the Optional object is null, the method will throw NoSuchElementException exception. This is completely contrary to our original intention to use the Optional class.

public class GetOptionalDemo {
    public static void main(String[] args) {
        String name = null;
        Optional<String> optOrNull = Optional.ofNullable(name);
        System.out.println(optOrNull.get());
    }
}

This program will throw an exception when running:

Exception in thread "main" java.util.NoSuchElementException: No value present
    at java.base/java.util.Optional.get(Optional.java:141)
    at com.cmower.dzone.optional.GetOptionalDemo.main(GetOptionalDemo.java:9)

Although the exception thrown is NoSuchElementException rather than NPE, in our opinion, it is obviously "five steps and a hundred steps." It is recommended that the orElseGet () method get the value of the Optional object.

08, filter value

Xiao Wang upgraded the previous code through the Optional class, and happily ran to find the old horse to complete the task. Lao Ma feels that this guy is good, flexible, active, and worth cultivating, so he gave Xiao Wang a new task: to check the length of the password when the user registers.

After Xiao Wang got the task, Le opened the flowers because he just had to learn the filter () method of the Optional class, which came in handy.

public class FilterOptionalDemo {
    public static void main(String[] args) {
        String password = "12345";
        Optional<String> opt = Optional.ofNullable(password);
        System.out.println(opt.filter(pwd -> pwd.length() > 6).isPresent());
    }
}

The parameter type of the filter () method is Predicate (a functional interface added in Java 8), which means that a Lambda expression can be passed to the method as a condition, and if the result of the expression is false, an EMPTY Optional object, otherwise it returns the filtered Optional object.

In the above example, because the length of the password is 5, the result of the program output is false. Assuming that the length of the password is required to be between 6 and 10 digits, then another condition can be added. Look at the code after Xiao Wang increased the difficulty.

Predicate<String> len6 = pwd -> pwd.length() > 6;
Predicate<String> len10 = pwd -> pwd.length() < 10;

password = "1234567";
opt = Optional.ofNullable(password);
boolean result = opt.filter(len6.and(len10)).isPresent();
System.out.println(result);

This time the result of the program output is true, because the password has become 7 digits, between 6 and 10 digits. Imagine how long the code would be if Xiao Wang used if-else to accomplish this task.

09, conversion value

After checking the length of the password, Xiao Wang still feels that it is not enough, and feels that the strength of the password must also be checked. For example, the password cannot be "password". Such a password is too weak. So he began to study the map () method, which can convert the original Optional object into a new Optional object according to certain rules, and the original Optional object will not change.

Let's first look at a simple example written by Xiao Wang:

public class OptionalMapDemo {
    public static void main(String[] args) {
        String name = "沉默王二";
        Optional<String> nameOptional = Optional.of(name);
        Optional<Integer> intOpt = nameOptional
                .map(String::length);

        System.out.println( intOpt.orElse(0));
    }
}

In the above example, the parameter String :: length of the map () method means that a new Optional object of type Integer should be generated according to the string length of the original string type Optional.

After figuring out the basic usage of the map () method, Xiao Wang decided to combine the map () method with the filter () method. The former is used to convert the password to lowercase, and the latter is used to determine the length and whether it is "password" .

public class OptionalMapFilterDemo {
    public static void main(String[] args) {
        String password = "password";
        Optional<String>  opt = Optional.ofNullable(password);

        Predicate<String> len6 = pwd -> pwd.length() > 6;
        Predicate<String> len10 = pwd -> pwd.length() < 10;
        Predicate<String> eq = pwd -> pwd.equals("password");

        boolean result = opt.map(String::toLowerCase).filter(len6.and(len10 ).and(eq)).isPresent();
        System.out.println(result);
    }
}
Published 238 original articles · Like 68 · Visits 30,000+

Guess you like

Origin blog.csdn.net/qq_45401061/article/details/104798446