1, Introduction
The Optional class is an Optional class with the same name introduced by Java 8 in order to solve the problem of null value judgment. Using the Optional class of google guava class library can avoid explicit null value judgment (null defensive check) and NPE (NullPointerException) caused by null.
Let's look at a piece of code:
public static String getGender(Student student) { if(null == student) { return "Unkown"; } return student.getGender(); }
This is a method to obtain the gender of students. The method input parameter is a student object. In order to prevent the student object from being null, a defensive check is made: if the value is null, it returns "Unkown".
Let's look at the method after using Optional Optimization:
public static String getGender(Student student) { return Optional.ofNullable(student).map(u -> u.getGender()).orElse("Unkown"); }
It can be seen that the use of Optional class combined with lambda expression can make the developed code more concise and elegant.
2, Creation of Optional objects
Let's take a look at some of the source code of the Optional class:
private static final Optional<?> EMPTY = new Optional<>(); private final T value; private Optional() { this.value = null; } public static<T> Optional<T> empty() { @SuppressWarnings("unchecked") Optional<T> t = (Optional<T>) EMPTY; return t; } private Optional(T value) { this.value = Objects.requireNonNull(value); } public static <T> Optional<T> of(T value) { return new Optional<>(value); } public static <T> Optional<T> ofNullable(T value) { return value == null ? empty() : of(value); }
It can be seen that the two construction methods of the Optional class are private, so they cannot be displayed outside the class. The new Optional() method is used to create the Optional object, but the Optional class provides three static methods: empty(), of(T value) and ofNullable(T value) to create the Optional object. The examples are as follows:
// 1. Create an Optional object with an empty wrapper object value Optional<String> optStr = Optional.empty(); // 2. Create an Optional object with a non empty wrapper object value Optional<String> optStr1 = Optional.of("optional"); // 3. Create an Optional object whose wrapper object value is allowed to be empty Optional<String> optStr2 = Optional.ofNullable(null);
3, Use of typical interfaces of Optional class
Below, take some typical scenarios as examples to list the usage of common interfaces of the Optional API and attach corresponding codes.
3.1 get() method
Take a brief look at the source code of the get() method:
public T get() { if (value == null) { throw new NoSuchElementException("No value present"); } return value; }
You can see that the get() method is mainly used to return the actual value of the wrapper object, but if the value of the wrapper object is null, a NoSuchElementException exception will be thrown.
3.2 isPresent() method
Source code of isPresent() method:
public boolean isPresent() { return value != null; }
As you can see, the isPresent() method is used to determine whether the value of the wrapper object is non empty. Let's look at a bad piece of code:
public static String getGender(Student student) { Optional<Student> stuOpt = Optional.ofNullable(student); if(stuOpt.isPresent()) { return stuOpt.get().getGender(); } return "Unkown"; }
This code implements the logic in Chapter 1 (Introduction), but this usage not only does not reduce the defensive check of null, but also increases the process of Optional packaging, which is contrary to the original intention of Optional design. Therefore, this bad use should be avoided in development~
3.3 ifPresent() method
Source code of ifPresent() method:
public void ifPresent(Consumer<? super T> consumer) { if (value != null) consumer.accept(value); }
The ifPresent() method accepts a Consumer object (Consumer function). If the value of the wrapper object is not empty, run the accept() method of the Consumer object. Examples are as follows:
public static void printName(Student student) { Optional.ofNullable(student).ifPresent(u -> System.out.println("The student name is : " + u.getName())); }
The above example is used to print the student name. Since the ifPresent() method has a null value check, there is no need to worry about NPE before calling.
3.4 filter() method
Source code of filter() method:
public Optional<T> filter(Predicate<? super T> predicate) { Objects.requireNonNull(predicate); if (!isPresent()) return this; else return predicate.test(value) ? this : empty(); }
The filter() method accepts a Predicate object as the parameter, which is used to filter the Optional object. If the Predicate conditions are met, the Optional object itself is returned; otherwise, an empty Optional object is returned. Examples are as follows:
public static void filterAge(Student student) { Optional.ofNullable(student).filter( u -> u.getAge() > 18).ifPresent(u -> System.out.println("The student age is more than 18.")); }
In the above example, the screening of students older than 18 is realized.
3.5 map() method
Source code of map() method:
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) return empty(); else { return Optional.ofNullable(mapper.apply(value)); } }
The parameter of the map() method is a Function (functional interface) object. The map() method uses the Function function Function to calculate the wrapped object in the Optional and wrap it into a new Optional object (the type of the wrapped object may change). Examples are as follows:
public static Optional<Integer> getAge(Student student) { return Optional.ofNullable(student).map(u -> u.getAge()); }
In the above code, first construct an Optional object with ofNullable() method, then calculate the age of students with map() and return the Optional object (if the student is null, the map() method returns an empty Optional object).
3.6 flatMap() method
Source code of flatMap() method:
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) return empty(); else { return Objects.requireNonNull(mapper.apply(value)); } }
Different from the map() method, the return value type of the input Function function is Optional < / U / > instead of U. in this way, flatMap() can map a two-dimensional Optional object to a one-dimensional object. Taking the example Function in 3.5 as an example, the fallmap() is rewritten as follows:
public static Optional<Integer> getAge(Student student) { return Optional.ofNullable(student).flatMap(u -> Optional.ofNullable(u.getAge())); }
3.7 orElse() method
Source code of orElse() method:
public T orElse(T other) { return value != null ? value : other; }
The function of the orElse() method is relatively simple, that is, if the value of the wrapper object is not empty, it returns the value of the wrapper object, otherwise it returns the value of the parameter other (the default value). As mentioned in Chapter 1 (Introduction):
public static String getGender(Student student) { return Optional.ofNullable(student).map(u -> u.getGender()).orElse("Unkown"); }
3.8 orElseGet() method
Source code of orElseGet() method:
public T orElseGet(Supplier<? extends T> other) { return value != null ? value : other.get(); }
The orElseGet() method is similar to the orElse() method, except that the input parameter of the orElseGet() method is a Supplier object, and the return value of the get() method of the Supplier object is used as the default value. For example:
public static String getGender(Student student) { return Optional.ofNullable(student).map(u -> u.getGender()).orElseGet(() -> "Unkown"); }
3.9 orElseThrow() method
Source code of orElseThrow() method:
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X { if (value != null) { return value; } else { throw exceptionSupplier.get(); } }
The orElseThrow() method is actually very similar to the orElseGet() method. The input parameters are Supplier objects, but the Supplier object of orElseThrow() must return a Throwable exception and throw the exception in orElseThrow():
public static String getGender1(Student student) { return Optional.ofNullable(student).map(u -> u.getGender()).orElseThrow(() -> new RuntimeException("Unkown")); }
The orElseThrow() method is applicable to the scenario where a specific exception needs to be thrown when the value of the wrapper object is null.
4, Precautions
When developing with Optional, you should pay attention to the correct use of the "posture" of Optional. In particular, you should not use the error demonstration mentioned in Section 3.2, use isPresent() and get() methods carefully, and use map(), filter(), orElse() and other methods as much as possible to play the role of Optional.