Article directory
- 1. Optional overview
- Two, Optional use
-
- 1. Create an Optional object
- 2. Application of isPresent() and ifPresent() & source code analysis
- 3. get() application & source code analysis
- 4. orElseThrow() application & source code analysis
- 5. map() application & source code analysis
- 6. flatMap() application & source code analysis
- 7. filter() application & source code analysis
- 8. orElse() application & source code analysis
- 9. orElseGet() application & source code analysis
- 10. orElseThrow() application & source code analysis
1. Optional overview
1. Annoying NullPointerException
In daily development, NullPointerException is believed to be seen by everyone, whether you are a newcomer or a hardcore player, you are familiar with it. It can be said to appear everywhere, and it can always appear in various scenarios. So how to prevent it from appearing, we usually passively adopt various non-null checks, but it still often appears in our sight.
public String getCompanyName(Student student){
if (student != null){
Job job = student.getJob();
if (job != null){
Company company = job.getCompany();
if (company != null){
String name = company.getName();
return name;
}else {
return "no company";
}
}else {
return "no job";
}
}else {
return "no student";
}
}
For the above code, I believe that you often have similar codes in your usual work. Every time an object is obtained, a null judgment is made, and then the subsequent implementation continues. But this method is very bad. First of all, there will be a large number of nested if-else judgments, resulting in extremely poor readability and scalability of the code. At this time, some students may use the guard statement to modify it, as follows:
public String getCompanyName(Student student){
if (student == null){
return "no student";
}
Job job = student.getJob();
if (job == null){
return "no job";
}
Company company = job.getCompany();
if (company == null){
return "no company";
}
return company.getName();
}
This kind of judgment has consciously avoided a large number of nested judgments, but there are also many different judgment points, and code maintenance is also difficult.
2. Introduction to Optional
In order to prevent the occurrence of null pointer exceptions, a new class has been introduced in Java8 Optional
, for which we have already implemented a simple implementation. Its essence is to encapsulate the value through the Optional class. When there is a value, the value will be encapsulated into the Optional class. If there is no value, an Empty will be encapsulated in this class.
Two, Optional use
Based on the Optional class 函数式接口
provides some methods for manipulating values.
1. Create an Optional object
Create Optional, which provides three method operations, namely: empty(), of(), ofNullable(). The way to use it is as follows:
Optional<Student> studentOptional = Optional.empty();
Optional<Student> studentOptional = Optional.of(student);
Optional<Student> studentOptional = Optional.ofNullable(student);
What is the difference between these three? According to the source code analysis is as follows:
// 源码分析
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY; // private static final Optional<?> EMPTY = new Optional<>();
return t;
}
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
According to the source code, empty()
an empty Optional instance will be returned directly, and there will be no value inside.
of() returns an Optional object with a value that does not allow nulls. If the method is called 传入参数是null,则立刻抛出NullPointerException
, instead of waiting until you use the object before throwing, it is equivalent to an immediate check.
ofNullable() will also return an Optional object with a value, but the biggest difference between it and of() is that it will judge the value passed in. If the value passed in is null, it will call empty() to return An Optional with no content. If not null, calling of() returns an Optional with content.
2. Application of isPresent() and ifPresent() & source code analysis
The Optional class provides two methods for judging whether the Optional has a value, respectively isPresent()和ifPresent(Consumer<? super T> consumer)
. It is generally used in conjunction with ofNullable(), because of() has already completed the judgment when it is created, and empty() simply instantiates an Optional object.
// 使用实例
public class PresentDemo {
public static void getStudentName(Student student){
Optional<Student> optional = Optional.ofNullable(student);
if (optional.isPresent()){
//student不为null
Student student1 = optional.get();
System.out.println(student1);
}else {
System.out.println("student为null");
}
optional.ifPresent(s-> System.out.println(s));
}
public static void main(String[] args) {
Student student = new Student(1,"zhangsan","M");
getStudentName(student);
}
}
// 源码分析
// isPresent()内部非常简单,就是判断这个值是否为null。
public boolean isPresent() {
return value != null;
}
public void ifPresent(Consumer<? super T> consumer) {
if (value != null)
consumer.accept(value);
}
When the ifPresent() method is executed, one is received consumer函数式接口
. If the value is not null, the value is obtained through the accept method in the consumer.
3. get() application & source code analysis
The use of get() is very simple, but it is not safe, because when obtaining the value, if the value exists, it will directly return the value encapsulated in the Optional, and if it does not exist, NoSuchElementException will be thrown. Therefore, the premise of its use is that it has been determined that there is a value in the Optional, otherwise it is useless.
// 使用实例
Optional<Student> studentOptional = Optional.ofNullable(student);
if (studentOptional.isPresent()){
Student result = studentOptional.get();
}
// 源码分析
public Tget() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
4. orElseThrow() application & source code analysis
This method is similar to get(), which is used to get values, but when there is no value in Optional, get() will directly throw NoSuchElementException. In this case, there are certain limitations, because sometimes it may be necessary to throw a self- Define exceptions. At this point, you can use orElseThrow(), which can throw a custom exception when there is no value in the Optional when taking the value.
// 使用实例
Optional<Student> optional = Optional.ofNullable(student);
try {
// null 就抛异常
Student result = optional.orElseThrow(MyException::new);
System.out.println(result);
} catch (MyException e) {
e.printStackTrace();
}
// 源码分析
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
// null 的话就抛异常
throw exceptionSupplier.get();
}
}
5. map() application & source code analysis
map() can implement type conversion, which is similar to the map of JDK8's Stream, except that one is to convert the generic type of Stream, and the other is to convert the generic type of Optional.
// 使用案例
if (studentOptional.isPresent()){
// Student类型转为String类型
Optional<String> nameOptional = studentOptional.map(Student::getName);
}
// 源码分析
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));
}
}
6. flatMap() application & source code analysis
The student's name has been obtained through map() just now, and the operation is very simple. But when a link is acquired, can the map be used? Such as: Student->Work->Company->Company Name.
Now you may have an idea in your head, that is, through map(), the code structure is as follows:
studentOptional.map(Student::getJob).map(Job::getCompany).map(Company::getName);
But this code cannot be compiled. Because according to the learning of map, every time it is called, the generic type of Optional will be changed, and finally a multi-layer Optional nested structure will be generated.
To solve this problem, the Optional class provides another method of obtaining values, flatMap(). It itself is used for multi-layer calls, and it does not form multiple Optionals for the results, but processes the results into a final type of Optional. But the return value obtained by flatMap must be of type Optional. Maps do not have this limitation.
// 使用实例
Optional<String> nameOptional = studentOptional.flatMap(Student::getJob).flatMap(Job::getCompany).map(Company::getName);
// 源码分析
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
// 多次调用不会生成Optional嵌套
return Objects.requireNonNull(mapper.apply(value));
}
}
7. filter() application & source code analysis
The method filter() provided in the Optional class 数据过滤
fulfills this requirement. It will judge according to the incoming conditions. If it matches, it will return an Optional object and contain the corresponding value, otherwise it will return a null Optional.
// 使用实例
Optional<Company> company = companyOptional.filter(c -> "bank".equals(c.getName()));
The source code is to pass in a Predicate. If it is not null, the test method of the predicate will be called to make a judgment.
// 源码分析
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
8. orElse() application & source code analysis
When fetching a value, if the value does not exist, sometimes we consider returning one 默认值
. This requirement can be realized through orElse().
It internally judges whether the value is null, if not, returns the value, and if it is null, returns the default value passed in.
// 使用案例
String value = studentOptional.flatMap(Student::getJob).flatMap(Job::getCompany).map(Company::getName).orElse("default value");
// 源码分析
publicT orElse(T other) {
// null的话取默认值
return value != null ? value : other;
}
9. orElseGet() application & source code analysis
orElseGet() is also a method used to return the default value when there is no value in the Optional. But it differs from orElse() in that it does 延迟加载
. Only called if there is no value in the Optional.
// 源码分析
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
// 代码案例
System.out.println(Optional.ofNullable("student").orElse(getStr("a")));
System.out.println(Optional.ofNullable(null).orElse(getStr("b")));
System.out.println(Optional.ofNullable("student").orElseGet(() ->getStr("a")));
System.out.println(Optional.ofNullable(null).orElseGet(() ->getStr("b")));
We found that when the data is not null, orElseGet will not be executed.
So when using it, it is more recommended to use orElseGet(), because it uses delayed calls so the performance is better.
10. orElseThrow() application & source code analysis
orElseThrow is very simple, if it is null, throw an exception.
// 源码分析
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}