Introduction to the

The optional class is an elegant solution to the NPE problem introduced in java8, and one that source authors hope will replace null.

history

In 1965, a British computer scientist named Tony Hoare came up with the idea of null references while designing ALGOL W. Hoare chose null references “just because it’s so easy to implement.” Years later, he came to regret his decision, calling it “my million-dollar mistake.” We’ve already seen what happens when a programmer checks an object’s field to see if its value is in the expected format, only to discover that instead of looking at an object, we’re looking at a null pointer, which immediately throws an annoying NullPointerException [1].

Problems with NULL

  • A source of error.

    NullPointerException is the most typical exception in Java program development today.

  • Code bloat.

    It leaves your code riddled with deeply nested null checks and horribly unreadable.

  • It’s meaningless on its own.

    Null has no semantics of its own; in particular, it represents the wrong way to model missing variable values in statically typed languages.

  • Broke the Java philosophy.

    Java tries to keep programmers from being aware of Pointers, with one exception: null Pointers.

  • A crack in the Java type system.

    Null is not of any type, which means it can be assigned to any variable that references a type. This can cause problems because when the variable is passed to another part of the system, you cannot know what type the null variable was originally assigned to.

plan

Taking inspiration from Haskell and Scala, a new class java.util.Optional<T> was introduced in Java 8. This is a class that encapsulates Optional values. For example, using the new class means that if you know that a person may or may not have a school, the school variable inside the Student class should not be declared Schoold, and a null reference should be assigned to it when encountering a Student who does not have a school. Instead, declare it as an Optional<School> type directly, as in this article.

The scene is introduced into

Let’s start with two common scenarios

/** * >1; /** * >1
public static boolean checkIsPublicV1(Student student) {
    if(student ! =null) {
        School school = student.getSchool();
        if(school ! =null) {
            returnschool.isPublicFlag(); }}throw new RuntimeException("Abnormal parameter");
}

public static String getAdultV1(Student student) {
    if(student ! =null) {
        int age = student.getAge();
        if (age > 18) {
            returnstudent.getName(); }}return "No";
}
Copy the code

The above approach is a common reading process. Optional is a solution to the problem of code appreciation caused by every null judgment

Method statement

The constructor

  • Optional(T var1)

The source code

private final T value;

private Optional(a) {
    this.value = null;
}

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

The optional constructor is private and cannot be created directly. It can only be created through other static methods in the class. The Optional constructor supports a generic input parameter and cannot be null

Create an Optional object

  • Declare an empty Optional: Optional

    empty()
  • Create Optional: Optional

    of(T var0) with a non-null value
  • Optional: Optional

    ofNullable(T var0)

The source code

The empty () the source code
private static finalOptional<? > EMPTY =new Optional();

private Optional(a) {
    this.value = null;
}

public static <T> Optional<T> empty(a) {
    Optional var0 = EMPTY;
    return var0;
}
Copy the code

The Optional class maintains a null object that can be returned using the empty static method

Source of var0 (T)
public static <T> Optional<T> of(T var0) {
    return new Optional(var0);
}
Copy the code

Return the usual parameter constructor. Note that since the constructor requires that the input parameter cannot be empty, an NPE exception will still be raised if the input parameter of the of method is empty

Var0 ofNullable (T) the source code
public static <T> Optional<T> ofNullable(T var0) {
    return var0 == null ? empty() : of(var0);
}
Copy the code

This method complements the ofNullable method by returning a constructor if the ofNullable parameter is not null, and an optional object with a null value when the ofNullable parameter is null. OfNullable is better than of in most scenarios.

Is there any essential difference between null references and option.empty ()? Semantically, you can think of them as the same thing, but in practice they are very different: If you try to dereference a NULL, NullPointerException will be triggered, but using option.empty () is completely fine. It is a valid object of the op-tional class and can be called in many situations, which is very useful.

For example,

public static void testOptionalBuild(a) {
    New 'Optional()' has private access in 'java.util.Optional'
    // Optional<School> school = new Optional<>();

    // 2. Of builds non-null and NullPointerException
    /*Optional
      
        o1 = Optional.of("test"); System.out.println(o1); Optional
        o2 = Optional.of(null); System.out.println(o2); * /
      

    // 3. OfNullable builds non-empty and empty objects (option.empty)
    /*Optional
      
        o3 = Optional.ofNullable("test"); System.out.println(o3); Optional
        o4 = Optional.ofNullable(null); System.out.println(o4); * /
      
}
Copy the code

Extract and convert values from Optional objects using Map

  • Optional<U> map(Function<? super T, ? extends U> var1)

The source code

public <U> Optional<U> map(Function<? super T, ? extends U> var1) {
    Objects.requireNonNull(var1);
    return !this.isPresent() ? empty() : ofNullable(var1.apply(this.value));
}
Copy the code

Empty objects are returned directly when the optional package is empty, otherwise the function.apply method in the input parameter is executed

Link Optional objects using flatMap

  • Optional<U> flatMap(Function<? super T, Optional<U>> var1)

The source code

public <U> Optional<U> flatMap(Function<? super T, Optional<U>> var1) {
    Objects.requireNonNull(var1);
    return !this.isPresent() ? empty() : (Optional)Objects.requireNonNull(var1.apply(this.value));
}
Copy the code

Almost the same as map. Note that in the Function. Apply method, the return type is optional

For example,

public static void testOptionalMap(a) {
    Student student1 = getDefaultStudent();
    Student student2 = getBackStudent();
    // map
    String school1 = Optional.ofNullable(student1).map(i -> i.getName()).orElse("Unknown");
    String school2 = Optional.ofNullable(student2).map(i -> i.getName()).orElse("Unknown");
    System.out.println("school1: " + school1 + "| school2: " + school2);
    / / flapMap chain
    String school3 = Optional.ofNullable(getOptionalStudent()).flatMap(i -> getOptionalStudent()).flatMap(i->i.getSchool()).map(i->i.getSchoolName()).orElse("Didn't go to college.");
    System.out.println("school3: " + school3);
}
Copy the code

Default behavior and dereference Optional object 1

  • T orElse(T var1)
  • T orElseGet(Supplier<? extends T> var1)
  • T orElseThrow(Supplier<? extends X> var1)

Note: These three methods are not static methods, so they need to be called from an instance object, usually followed by the ofNullable method to handle null or return values

The source code

OrElse the source code
public T orElse(T var1) {
    return this.value ! =null ? this.value : var1;
}
Copy the code

Returns the package value in Optional if the package value is not empty, or in orElse if it is empty

OrElseGet source
public T orElseGet(Supplier<? extends T> var1) {
    return this.value ! =null ? this.value : var1.get();
}
public T get(a) {
    if (this.value == null) {
        throw new NoSuchElementException("No value present");
    } else {
        return this.value; }}Copy the code

Similar to the previous method, returns the package value if the package value in Optional is not empty, or executes the Supplier method in orElseGet if it is empty

OrElseThrow source
public <X extends Throwable> T orElseThrow(Supplier<? extends X> var1) throws X {
    if (this.value ! =null) {
        return this.value;
    } else {
        throw(Throwable)var1.get(); }}Copy the code

Similarly, return the package value in Optional if the package value is not null, or throw the Supplier. Get exception method in orElseThrow if the package value is null

For example,

public static void testOptionalOrElse(a) {
    // orElse
    Student stu = getDefaultStudent();
    Student backStudent = getBackStudent();
    Student realStu1 = Optional.ofNullable(stu).orElse(backStudent);
    System.out.println(realStu1);

    // orElseGet
    Student realStu2 = Optional.ofNullable(stu).orElseGet(()-> getBackStudent());
    System.out.println(realStu2);

    // orElseGet
    Student realStu3 = Optional.ofNullable(stu).orElseThrow(()-> new RuntimeException("Students do not exist."));
    System.out.println(realStu3);
}
Copy the code

Default behavior and dereference Optional object 2

  • boolean isPresent()
  • void ifPresent(Consumer<? super T> var1)

The source code

IsPresent () the source code
public boolean isPresent(a) {
    return this.value ! =null;
}
Copy the code

The user determines whether the value of the optional package is empty and returns a Boolean value

IfPresent (Consumer var1) source
public void ifPresent(Consumer<? super T> var1) {
    if (this.value ! =null) {
        var1.accept(this.value); }}Copy the code

If the value of the optional package is not null, the user continues processing the consumer.accept method in the input parameter. Similar to the if (var. =null) {do sth}

For example,

public static void testOptionalIfPresent(a) {
    // isPresent()
    Student student1 = getDefaultStudent();
    Student student2 = getBackStudent();
    boolean b1 = Optional.ofNullable(student1).isPresent();
    boolean b2 = Optional.ofNullable(student2).isPresent();
    System.out.println("b1: " + b1 + "| b2: " + b2);

    // isPresent(Consumer)
    Optional.ofNullable(student2).ifPresent(i-> acceptStudent(i, LocalDate.now()));
}
Copy the code

Use filter to filter out specific values

  • Optional filter(Predicate<? super T> var1)

The source code

public Optional<T> filter(Predicate<? super T> var1) {
    Objects.requireNonNull(var1);
    if (!this.isPresent()) {
        return this;
    } else {
        return var1.test(this.value) ? this: empty(); }}Copy the code

Filter optional objects. Returns the optional package value if it is not null, or executes the Predicate. Test method of the filter entry

For example,

public static void testOptionalFilter(a) {
    Student student1 = getDefaultStudent();
    Student student2 = getBackStudent();
    System.out.println(student1);
    System.out.println(student2);
    Student student3 = Optional.ofNullable(student1).filter(i -> i.getAge() > 18).orElse(getBackStudent());
    Student student4 = Optional.ofNullable(student2).filter(i -> i.getAge() > 18).orElse(getBackStudent());
    System.out.println(student3);
    System.out.println(student4);
}
Copy the code

In actual combat

The optional class has been explained for the most part. Back to the beginning, the scene mentioned is introduced and transformed with Optional

/** * Update the original checkIsPublicV1 */
public static boolean checkIsPublicV2(Student student) {
    return Optional.ofNullable(student).map(i -> i.getSchool()).map(i -> i.isPublicFlag()).orElseThrow(() -> new RuntimeException("Abnormal parameter"));
}

/** * getAdultV1 */
public static String getAdultV2(Student student) {
    return Optional.ofNullable(student).filter(i->i.getAge()>18).map(i->i.getName()).orElseGet(()->getDefaultStudent().getName());
}
Copy the code

The attached:

Add the code

public static void main(String[] args) {
    // Release one by one
    / / introduction
// System.out.println(checkIsPublicV1(stu2));
// System.out.println(getAdultV1(stu2));
    / / optional methods
// testOptionalBuild();
// testOptionalOrElse();
// testOptionalIfPresent();
// testOptionalMap();
// testOptionalFilter();
    / / field
// System.out.println(getAdultV2(stu3));
// System.out.println(checkIsPublicV2(stu3));
}

/**======== model data =======**/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
static class Student {
    private String name;
    private int age;
    private School school;
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
static class School {
    private String schoolName;
    private boolean publicFlag;
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
static class StudentOpt {
    private String name;
    private int age;
    private Optional<School> school;
}

public static Student getDefaultStudent(a) {
    return null;
}

public static Student getBackStudent(a) {
    return Student.builder().name("Little red").age(19).build();
}

public static Optional<StudentOpt> getOptionalStudent(a) {
    return Optional.ofNullable(StudentOpt.builder().name("Little mo").age(18)
            .school(Optional.ofNullable(School.builder().schoolName(The University of Blue Whale).publicFlag(true).build())).build());
}

public static void acceptStudent(Student stu, LocalDate date) {
    System.out.println("Date:" + date + "A new student:" + stu.getName());
}
Copy the code

[1] Java8

For detailed source code, please refer to: github.com/chetwhy/clo…