java Foundation Series 16 (Optional detailed explanation)

Keywords: Java

catalogue

1, Overview

2, Optional

2.1 creating an Optional instance

2.2 intermediate operation - processing Optional

2.3 obtaining target values from Optional

3, Mode of use

4, Summary

1, Overview

The introduction of Optional is to solve the problem of null, so what is the problem of null?

We know that when we call a method for null, we will throw a null pointer exception. Optional is designed to solve this problem.

Option is expressed by encapsulating the target object. When we use it, option must exist, because if the result is null, a fixed EMPTY instance will be returned, so there will be no problem of null reference.

How do you use Optional?

We can't abuse option. We can use option to encapsulate the target object only when it is logically (business) possible to be null. If it is definitely not logically (business) possible to be null, we don't use option to encapsulate the object. In this way, when the code reader sees option, it means that the target object can be null.

After that, if there is a null pointer exception in the program, it can only be the logical error of your code, not the non logical reason, that is, null occurs when it should not be null, which must be your logical mistake.

2, Optional

2.1 creating an Optional instance

2.1.1 create an empty Optional

public class OptionalTest {
    public static void main(String[] args) {
        Optional<String> o = Optional.empty();// Create an empty Optional
    }
}

2.1.2 create an Optional with value

public class OptionalTest {
    public static void main(String[] args) {
        Optional<String> op = Optional.of("123");// Creating a target object must have an Optional value
    }
}

In this way, if the parameter of is null, an exception will be thrown directly.

2.1.3 create an Optional that can be empty

public class OptionalTest {
    public static void main(String[] args) {
        Optional<String> opt = Optional.ofNullable(null);// Create an Optional with null able target object
    }
}

2.2 intermediate operation - processing Optional

2.2.1 filter

This filter is a verifier. Since there is only one element in Optional, it is too big to call it filtering. If the element in Optional meets the given verification conditions, it will return the option of the encapsulated element, otherwise it will return an empty option.

public class OptionalTest {
    public static void main(String[] args) {
        filterTest();
    }
    public static void filterTest(){
        Optional<String> os = Optional.of("123456").filter(e -> e.length()>7);
        System.out.println(os.isPresent());
    }
}

Execution results:

false

Because the length of the string "123456" is less than 7, an empty Optional is returned.

2.2.2 map

map means mapping, which is to operate on the elements encapsulated by option, and then return a new option encapsulated with the operation result.

public class OptionalTest {
    public static void main(String[] args) {
        mapTest();
    }
    public static void mapTest(){
        Optional<Integer> oi = Optional.of("abcdefg").map(e -> e.length());
        System.out.println(oi.get());
    }
}

Execution results:

7

2.2.3 flatMap

This method is flat mapping. The so-called flat mapping is to deal with nested options.

For example, there is a Person class with an Optional field name. We obtained an instance of the Optional type elsewhere. Now we want to obtain the name of the Person, which is the nested option.

class Person{
    Optional<String> name;
    public Person(String name){
        this.name = Optional.of(name);
    }

    public Optional<String> getName(){
        return name;
    }
}

If we use map to get the following:

public class OptionalTest {
    public static void main(String[] args) {
        flatMapTest();
    }
    public static void flatMapTest(){
        Optional<Person> op = Optional.of(new Person("huahua"));
        Optional<Optional<String>> oos = op.map(Person::getName);
        String name = oos.get().orElseGet(()->"noName");
        System.out.println(name);
    }
}

If we use flatMap, it can be as follows:

public class OptionalTest {
    public static void main(String[] args) {
        flatMapTest();
    }
    public static void flatMapTest(){
        Optional<Person> op = Optional.of(new Person("huahua"));
        Optional<String> os = op.flatMap(Person::getName);
        String name = os.orElseGet(()->"noName");
        System.out.println(name);
    }
}

Nested options must be shelled many times to obtain the target object. This is also the principle of using map mapping, but using flatMap and flattening function, one shelling operation is completed.

2.3 obtaining target values from Optional

There are four ways:

  • public T get(): directly get the value in Optional. If it is empty, throw NoSuchElementException
  • public T orElse(T other): if Optional is not empty, the value will be returned; otherwise, the specified other (default value) will be returned
  • Public t orelseget (supplier <? Extensions T > other): if Optional is not empty, the value will be returned; otherwise, the value generated by other will be returned in the specified way
  • public   T orelsethrow (supplier <? Extensions x > exceptionSupplier) throws X: if Optional is not empty, return the value; otherwise, throw the exception generated by the specified exceptionSupplier
public class OptionalTest {
    public static void main(String[] args) {
        getTest();
        orElseTest();
    }
    public static void getTest(){
        Optional<String> os = Optional.of("123456");
        System.out.println(os.get());
    }
    public static void orElseTest(){
        Optional<String> os = Optional.empty();
        System.out.println(os.orElse("default"));
        System.out.println(os.orElseGet(()->"default"));
        System.out.println(os.orElseThrow(RuntimeException::new));
    }
}

Execution results:

123456
default
default
Exception in thread "main" java.lang.RuntimeException
	at java.util.Optional.orElseThrow(Optional.java:290)
	at com.dh.stream.OptionalTest.orElseTest(OptionalTest.java:29)
	at com.dh.stream.OptionalTest.main(OptionalTest.java:17)

3, Mode of use

You can't get directly when using Optional. At this time, you will go into the old way. When getting, it may be null and an exception will be thrown. At this time, you will want to judge isPresent before getting. What's the difference between not using Optional.

We use option to simplify null judgment, so we refuse to use the get method. The correct methods provided by option are orElse, orElseGet and orElseThrow.

Using the orElse method, we can get the value from the non empty option. If it is empty, we can return the default value specified by the orElse method parameter.

Using the orElseGet method, we can actively build a default return result in the case of empty Optional.

The orElseThrow method will throw a specified exception when it is empty and Optional.

4, Summary

Having said so much, we finally want to use Optional in development. As we said at the beginning, we should make it clear that Optional avoids null and cannot be used everywhere.

Under the business rules of the project, an object may be null (that is, null allowed by the business). In this case, null is a normal phenomenon. This situation needs to be avoided. We use Optional to encapsulate the target object to ensure that null calls will not throw null pointers.

However, if an object cannot be null under business rules (that is, the business does not allow null), in this case, null is an error in the program, which is not a normal phenomenon. At this time, we can't use Optional to encapsulate the target object to avoid the problem, but use it directly. Once an error occurs, we can troubleshoot the problem in time, The problem will not be hidden by Optional.

Posted by sliilvia on Mon, 18 Oct 2021 17:06:54 -0700