Easy practical understanding Optional
1. Preface
I believe many of my friends have been dazed by the so-called Null Pointer Exception in java's NPE(Null Pointer Exception). Some big guys said that "preventing NPE is the basic cultivation of programmers." but cultivation is also one of our most troublesome problems for programmers, So today, we will try our best to make use of the new feature Optional of java 8 to simplify the code as much as possible and deal with NPE(Null Pointer Exception) efficiently
2. Know and use Optional
In short, the opital class is provided by Java. In order to solve the problem of judging whether the object is empty, you will use null= The existence of obj is a headache, resulting in NPE (Null Pointer Exception). At the same time, the existence of Optional can make the code simpler, more readable and more efficient
General judgment: //Elephant Man //The attributes are name and age Person person=new Person(); if (null==person){ return "person by null"; } return person; Copy code
use Optional: //Elephant Man //The attributes are name and age Person person=new Person(); return Optional.ofNullable(person).orElse("person by null"); Copy code
Test and display class Person code (if a friend doesn't understand, you can take a look at this):
public class Person { private String name; private Integer age; public Person(String name, Integer age) { this.name = name; this.age = age; } public Person() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } } Copy code
Next, let's efficiently learn about the magical Optional class!
2.1 creation of optional objects
First, let's open the interior of Optional to find out First, extract several methods to create Optional objects
public final class Optional<T> { private static final Optional<?> EMPTY = new Optional<>(); private final T value; //We can see that both construction squares are private //It means we can't go outside to new and come out with Optional objects private Optional() { this.value = null; } private Optional(T value) { this.value = Objects.requireNonNull(value); } //This static method basically creates an object with an empty wrapper value because there is no parameter assignment public static<T> Optional<T> empty() { @SuppressWarnings("unchecked") Optional<T> t = (Optional<T>) EMPTY; return t; } //This static method basically creates an object with a non empty packing value because of the assignment public static <T> Optional<T> of(T value) { return new Optional<>(value); } //This static method basically creates an empty object if the parameter value is empty, and creates a parameterized object if it is not empty public static <T> Optional<T> ofNullable(T value) { return value == null ? empty() : of(value); } } Copy code
Then make a simple example to show the correspondence with the above
// 1. Create an Optional object with an empty wrapper object value Optional<String> optEmpty = Optional.empty(); // 2. Create an Optional object with a non empty wrapper object value Optional<String> optOf = Optional.of("optional"); // 3. Create an Optional object whose wrapper object value can or cannot be empty Optional<String> optOfNullable1 = Optional.ofNullable(null); Optional<String> optOfNullable2 = Optional.ofNullable("optional"); Copy code
We have roughly analyzed the internal methods of creating Optional objects, and then formally enter the learning and use of Optional
2.2 Optional.get() method (return the value of the object)
The get() method returns an instance value of option Source code:
public T get() { if (value == null) { throw new NoSuchElementException("No value present"); } return value; } Copy code
That is, if value is not empty, it will be returned. If it is empty, an exception "No value present" will be thrown Simple example display
Person person=new Person(); person.setAge(2); Optional.ofNullable(person).get(); Copy code
2.3 Optional.isPresent() method (judge whether it is empty)
isPresent() method will return a boolean value. If the object is not empty, it will be true. If it is empty, it will be false Source code:
public boolean isPresent() { return value != null; } Copy code
Simple example:
Person person=new Person(); person.setAge(2); if (Optional.ofNullable(person).isPresent()){ //Write non empty logic System.out.println("Not empty"); }else{ //Write empty logic System.out.println("Empty"); } Copy code
2.4 Optional.ifPresent() method (judge whether it is empty and return the function)
This means that if the object is not empty, the function body is run Source code:
public void ifPresent(Consumer<? super T> consumer) { //If value is not empty, run the accept method body if (value != null) consumer.accept(value); } Copy code
See the example:
Person person=new Person(); person.setAge(2); Optional.ofNullable(person).ifPresent(p -> System.out.println("Age"+p.getAge())); Copy code
If the object is not null, the age will be printed. Because NPE (non null judgment) has been made internally, there is no need to worry about null pointer exceptions
2.5 Optional.filter() method (filter object)
The filter() method roughly means to accept an object and filter it conditionally. If the conditions are met, the Optional object itself is returned. If not, the empty Optional object is returned Source code:
public Optional<T> filter(Predicate<? super T> predicate) { Objects.requireNonNull(predicate); //If it is empty, this is returned directly if (!isPresent()) return this; else //Determine whether the return itself is empty or Optional return predicate.test(value) ? this : empty(); } Copy code
Simple example:
Person person=new Person(); person.setAge(2); Optional.ofNullable(person).filter(p -> p.getAge()>50); Copy code
2.6 Optional.map() method (secondary packaging of objects)
The map() method performs a secondary operation on the object in the corresponding function interface, encapsulates it into a new object, and then returns it in Optional Source code:
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) { Objects.requireNonNull(mapper); //If it is empty, return to yourself if (!isPresent()) return empty(); else { //Otherwise, the Optional modified by the method is returned return Optional.ofNullable(mapper.apply(value)); } } Copy code
Example display:
Person person1=new Person(); person.setAge(2); String optName = Optional.ofNullable(person).map(p -> person.getName()).orElse("name Empty"); Copy code
2.7 Optional.flatMap() method (secondary packaging of Optional objects)
The map() method performs a secondary operation on the object in the corresponding Optional < function > functional interface, encapsulates it into a new object, and then returns it in Optional Source code:
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)); } } Copy code
example:
Person person=new Person(); person.setAge(2); Optional<Object> optName = Optional.ofNullable(person).map(p -> Optional.ofNullable(p.getName()).orElse("name Empty")); Copy code
2.8 Optional.orElse() method (null return object)
One of the common methods. This method means that if the wrapper object is empty, the value in the orElse method will be executed. If it is not empty, the write object will be returned Source code:
public T orElse(T other) { //If it is not empty, return value; if it is empty, return other return value != null ? value : other; } Copy code
example:
Person person1=new Person(); person.setAge(2); Optional.ofNullable(person).orElse(new Person("Xiao Ming", 2)); Copy code
2.9 Optional.orElseGet() method (return Supplier object if null)
This is very similar to orElse. The input parameter is different. The input parameter is a Supplier object. If it is empty, it returns the. get() method of the incoming object. If it is not empty, it returns the current object Source code:
public T orElseGet(Supplier<? extends T> other) { return value != null ? value : other.get(); } Copy code
example:
Optional<Supplier<Person>> sup=Optional.ofNullable(Person::new); //Call the get() method, then the constructor of the object will be called, that is, the real object will be obtained Optional.ofNullable(person).orElseGet(sup.get()); Copy code
To be honest, I was confused about the Supplier object. I learned that the Supplier is also a way to create objects by simply looking up it on the Internet. In short, the Supplier is an interface, which is a lazy load similar to Spring. After declaration, it will not occupy memory. Only after executing the get () method, The constructor will be called to create the object. If the syntax of creating the object is Supplier < person > supperson = person:: new; supPerson.get() can be used if necessary
2.10 Optional.orElseThrow() method (null returns an exception)
Personally, I often use this method in practice. The function of this method is to throw the exception you defined if it is empty. If it is not empty, return the current object. In practice, all exceptions must be handled well for the sake of code readability Source code:
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X { if (value != null) { return value; } else { throw exceptionSupplier.get(); } } Copy code
Example: This is the actual combat source code
//A simple query Member member = memberService.selectByPhone(request.getPhone()); Optional.ofNullable(member).orElseThrow(() -> new ServiceException("There is no relevant data to query")); Copy code
2.11 comparative analysis with similar methods
Maybe if you haven't used it, you'll think orElse() is very similar to orElseGet() and orelsethow(). map() is very similar to flatMap(). Hahaha, don't worry. They all come from this step. I'll summarize the similarities and differences of different methods Similarities and differences between orElse() and orElseGet() and orElseThrow()
The effect of the method is similar. If the object is not empty, the object is returned. If it is empty, the corresponding parameters in the method body are returned. Therefore, it can be seen that the parameters in the three method bodies are different orElse (t object) orElseGet (supplier < T > object) orElseThrow (exception)
Similarities and differences between map() and orElseGet
The effect of the method is similar. The method parameters are repackaged and returned, with different map (function function) and flatmap (optional < function > function) parameters
How to use it should be defined according to the business scenario and code specification. Here's a brief look at how I use the magical option in practice
3. Reproduction of actual combat scene
Scenario 1: query an object in the service layer, judge whether it is empty and handle it after returning
//Query an object Member member = memberService.selectByIdNo(request.getCertificateNo()); //Use ofNullable plus orElseThrow for judgment and operation Optional.ofNullable(member).orElseThrow(() -> new ServiceException("There is no relevant data to query")); Copy code
Scenario 2: we can add Optional when defining the return value in the dao interface layer. For example, I use jpa, and so do others
public interface LocationRepository extends JpaRepository<Location, String> { Optional<Location> findLocationById(String id); } Copy code
However, in the Service
public TerminalVO findById(String id) { //This method is also wrapped with Optional in dao layer Optional<Terminal> terminalOptional = terminalRepository.findById(id); //Directly use isPresent() to determine whether it is empty if (terminalOptional.isPresent()) { //Use the get() method to get the object value Terminal terminal = terminalOptional.get(); //In actual combat, we have eliminated the cumbersome task of using set to assign values, and directly use BeanCopy to assign values TerminalVO terminalVO = BeanCopyUtils.copyBean(terminal, TerminalVO.class); //Call dao layer method to return wrapped object Optional<Location> location = locationRepository.findLocationById(terminal.getLocationId()); if (location.isPresent()) { terminalVO.setFullName(location.get().getFullName()); } return terminalVO; } //Don't forget to throw an exception throw new ServiceException("The terminal does not exist"); } Copy code
There are many actual combat scenarios, including whether to return the current value or jump to another method body when returning. If you have no experience and want to learn, you can comment and I will reply to you
4. Precautions for optional use
Optional is really easy to use. Can it completely replace if judgment? I think this must be the idea that you may have after using optional. The answer is No Take the simplest Chestnut: Example 1: what if I just want to judge whether a variable of the object is empty and make a judgment?
Person person=new Person(); person.setName(""); persion.setAge(2); //General judgment if(StringUtils.isNotBlank(person.getName())){ //Execute code block whose name is not empty } //Use Optional for judgment Optional.ofNullable(person).map(p -> p.getName()).orElse("name Empty"); Copy code
I think this example can well illustrate this problem. It's just a very simple judgment. if Optional is used, we also need to consider the packaging value, code writing and method call. Although there is only one line, the readability is not good. if other programmers read it, I don't think it's obvious
5.jdk1.9 optimization of Optional
First, three methods are added: Or(), ifpresentoelse(), and stream(). or() Similar to orelse and other methods, if the object is not empty, it returns the object. If it is empty, it returns the preset value in the or () method. ifPresentOrElse() Method has two parameters: a Consumer and a Runnable. If the object is not empty, the action of Consumer will be executed; otherwise, Runnable will be run. There are more orelse judgments than ifPresent()** Stream() * * converts Optional into stream. If there is a value, the stream containing the value is returned. If there is no value, the empty stream is returned.
Because I didn't test the specific jdk1.9 option, and I also found that there are good articles that can let you understand the optimization of jdk1.9 option, so I won't go into it.