introduction
NPE (NullPointerException) is the most common exception in debugging programs. Google has a lot of discussions about whether the method should return null or new an empty object.
At the beginning of the article, let’s talk about the NPE problem first. The NPE problem is the NullPointerException that we often encounter in development. Suppose we have two classes, and their UML class diagram is shown in the following figure
In this case, there is the following code
user.getAddress().getProvince();
This way of writing may report a NullPointerException when user is null. In order to solve this problem, the following writing method is adopted
if(user!=null){ Address address = user.getAddress(); if(address!=null){ String province = address.getProvince(); }}This writing style is relatively ugly. In order to avoid the above ugly writing style, the ugly design becomes elegant. JAVA8 provides Optional class to optimize this writing method, and the following text section will be explained in detail.
API introduction
Let me first introduce the API. Unlike other articles, this article adopts an analogy method and combines the source code. Unlike other articles, each API lists makes people unable to find the key points.
(1) Optional(T value), empty(), of(T value), ofNullable(T value)
These four functions have correlations, so they are placed in a group for memory.
Let me first explain that Optional(T value), that is, the constructor, is private permission and cannot be called externally. The other three functions are public permissions for us to call. Then, the essence of Optional is to store a real value internally, and when constructing, it is directly judged whether its value is empty. OK, that's still quite abstract. Directly upload the source code of the Optional(T value) constructor, as shown in the figure below
Then, the source code of (T value) is as follows
public static <T> Optional<T> of(T value) { return new Optional<>(value); } In other words, the constructor is called internally by the of(T value) function. Based on the source code of the constructor, we can draw two conclusions:
(1) When the value of the value is empty, a NullPointerException will still be reported.
(2) The Optional object constructed by the of(T value) function can be constructed normally when the value value is not empty.
In addition, the Optional class also maintains an object with value null, probably like the following
public final class Optional<T> { //Omit...... private static final Optional<?> EMPTY = new Optional<>(); private Optional() { this.value = null; } //Omit... public static<T> Optional<T> empty() { @SuppressWarnings("unchecked") Optional<T> t = (Optional<T>) EMPTY; return t; }} Then, the function of empty() is to return the EMPTY object.
Well, so many preparations have been laid. It can be said that ofNullable(T value) has the function, and the source code is added.
public static <T> Optional<T> ofNullable(T value) { return value == null ? empty() : of(value); } Well, everyone should understand what it means. The difference compared to of(T value) is that when the value value is null, of(T value) will report a NullPointerException; ofNullable(T value) will not throw Exception, ofNullable(T value) directly returns an EMPTY object.
Does that mean that we only use of Nullable function instead of of function in our project?
No, if something exists, then it will naturally have value. When we are running, we don't want to hide NullPointerException. Instead, you need to report immediately, and in this case, use the Of function. But I have to admit that there are really few such scenes. The blogger has only used this function in writing junit test cases.
(2) orElse(T other), orElseGet(Supplier<? extends T> other) and orElseThrow(Supplier<? extends X> exceptionSupplier)
These three functions are memorized in a group, and are called when the value passed in by the constructor is null. The usage of orElse and orElseGet is as follows. When the value is null, a default value is given:
@Testpublic void test() { User user = null; user = Optional.ofNullable(user).orElse(createUser()); user = Optional.ofNullable(user).orElseGet(() -> createUser()); }public User createUser(){ User user = new User(); user.setName("zhangsan"); return user;} The difference between these two functions: when the user value is not null, the orElse function will still execute the createUser() method, while the orElseGet function will not execute the createUser() method, so you can test it yourself.
As for orElseThrow, when the value is null, an exception is thrown out directly. The usage is as follows
User user = null;Optional.ofNullable(user).orElseThrow(()->new Exception("User does not exist"));(3) map(Function<? super T, ? extends U> mapper) and flatMap(Function<? super T, Optional<U>> mapper)
These two functions are placed in a set of memory, and these two functions do the operation of converting values.
Directly upload source code
public final class Optional<T> { //Omit...... 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)); } } //Omit... 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)); } }} There is no difference between these two functions in the body of the function. The only difference is the entry parameter. The entry parameter type accepted by the map function is Function<? super T, ? extends U>, while the entry parameter type of flapMap is Function<? super T, Optional<U>>.
In terms of specific usage, for map:
If the User structure is as follows
public class User { private String name; public String getName() { return name; }}At this time, the writing method of taking name is as follows
String city = Optional.ofNullable(user).map(u-> u.getName()).get();
For flatMap:
If the User structure is as follows
public class User { private String name; public Optional<String> getName() { return Optional.ofNullable(name); }}At this time, the writing method of taking name is as follows
String city = Optional.ofNullable(user).flatMap(u-> u.getName()).get();
(4) isPresent() and ifPresent(Consumer<? super T> consumer)
Put these two functions together to memorize. isPresent means to determine whether the value is empty, and ifPresent means to do some operations when the value is not empty. The source codes of these two functions are as follows
public final class Optional<T> { //Omit...... public boolean isPresent() { return value != null; } //Omit... public void ifPresent(Consumer<? super T> consumer) { if (value != null) consumer.accept(value); }}It needs additional explanation, don't
if (user != null){ // TODO: do something}Written
User user = Optional.ofNullable(user);if (Optional.isPresent()){ // TODO: do something} Because of this, the code structure is still ugly. The blogger will give the correct writing method later
As for ifPresent(Consumer<? super T> consumer), the usage is also very simple, as shown below
Optional.ofNullable(user).ifPresent(u->{ // TODO: do something });(5) filter(Predicate<? super T> predictate)
Without further ado, just upload the source code
public final class Optional<T> { //Omit...... Objects.requireNonNull(predicate); if (!isPresent()) return this; else return predict.test(value) ? this : empty();} The filter method accepts a Predicate to filter the values contained in Optional. If the included values meet the conditions, then the Optional will still be returned; otherwise, the Optional.empty will be returned.
The usage is as follows
Optional<User> user1 = Optional.ofNullable(user).filter(u -> u.getName().length()<6);
As shown above, if the length of user's name is less than 6, then return. If it is greater than 6, an EMPTY object is returned.
Practical use
Example 1
In function method
Previous writing
public String getCity(User user) throws Exception{ if(user!=null){ if(user.getAddress()!=null){ Address address = user.getAddress(); if(address.getCity()!=null){ return address.getCity(); } } } throw new Expetion("Value error"); }JAVA8 writing method
public String getCity(User user) throws Exception{ return Optional.ofNullable(user) .map(u->u.getAddress()) .map(a->a.getCity()) .orElseThrow(()->new Exception("Fetch error"));}Example 2
For example, in the main program
Previous writing
if(user!=null){ dosomething(user);}JAVA8 writing method
Optional.ofNullable(user) .ifPresent(u->{ dosomething(u); });Example 3
Previous writing
public User getUser(User user) throws Exception{ if(user!=null){ String name = user.getName(); if("zhangsan".equals(name)){ return user; } }else{ user = new User(); user.setName("zhangsan"); return user; }}Java8 writing method
public User getUser(User user) { return Optional.ofNullable(user) .filter(u->"zhangsan".equals(u.getName())) .orElseGet(()-> { User user1 = new User(); user1.setName("zhangsan"); return user1; });}Other examples will not be listed one by one. However, the blogger believes that using this chain programming is actually elegant in the code. However, the logic is not so obvious and the readability is reduced. We use it according to the situation in the project.
Summarize
The above is the entire content of this article. I hope that the content of this article has certain reference value for everyone's study or work. If you have any questions, you can leave a message to communicate. Thank you for your support to Wulin.com.