1 Introduction
As a Java programmer, whether you are a rookie or a master who has experienced the world, you must have encountered all kinds of abnormal errors. In an article abroad, statistics on the abnormal types of rankings, as shown below:
Yes, you read that right, it is at the NullPointerException
top of the list.
Null Reference
The inventor Charles Antony Richard Hoare said: “I call it my billion-dollar mistake. This is the result of the invention of null references in 1965... This has led to countless mistakes, bugs, and system crashes in the last 40 years. It may cause billions of dollars in pain and destruction."
This seems a bit exaggerated, but there is no dispute that it is NullPointerException
simply a pain in the programmer's heart, not to say how difficult it is to solve, but to solve it we need to pay an extra price.
I still remember NullPointerException
the bugs that you encountered when you first entered the industry in three days . After solving one, you encountered it in another place. This will gradually let you know, don’t trust any “objects”, especially those provided to you by others, and add judgment wherever you use them, so you can feel more at ease. So the code usually becomes the following:
pre lang="text" data-origin="pm_code_preview" style="margin: 0px; padding: 0px; list-style: none; font-family: Courier, "Courier New", monospace; display: block;">
String name = "Unknown";
if (null != people) {
if (null != people.getName()) {
name = people.getName();
}
}
return name;
In this way, although there is no need to worry NullPointerException
, too many judgment statements really make the scalp numb, and the code becomes bloated. If the object is too complex, there are objects in the object, etc., do you continue to judge layer by layer?
What's exciting is that JDK1.8 introduced a new class java.util.Optional<T>
. With the API provided by the Optional class, we no longer have to worry, let alone NullPointerException
write those annoying judgments.
2. Optional class
For example, using a new class means that if you know that a person may or may not have a car, then the car variable inside the Person class should not be declared as Car, and assign a null reference to it when someone does not have a car. It should be directly declared as an Optional type as shown in the figure below.
When the variable exists, Optional
the class simply encapsulates the class. When the variable does not exist, the missing value will be modeled as an "empty" Optional
object, which is Optional.empty()
returned by the method . Optional.empty()
The method is a static factory method that returns a specific single instance of the Optional class.
Optional
, Is essentially a container object, with a non-null or null value, we need to pass the object instance into the container. If the value exists, the Optional.isPresent()
method returns true
and the value is obtained through the Optional.get()
method.
Optional
The construction method is private
that you cannot directly use new to create an Optional
object, but can only use the Optional
provided static method to create it.
The creation methods provided by Optional are as follows:
-
Optional.of(obj)
: If the object isnull
, it will be thrownNullPointerException
. -
Optional.ofNullable(obj)
: If the object is null, an EMPTYOptional
object instance without a value will be created (new Optional<>()). -
Optional.empty()
: Same asOptional.ofNullable(null)
.
Among them, the source code fragment is as follows:
<pre lang="text" data-origin="pm_code_preview" style="margin: 0px; padding: 0px; list-style: none; font-family: Courier, "Courier New", monospace; display: block;">
/**
* Constructs an instance with the value present.
*
* @param value the non-null value to be present
* @throws NullPointerException if value is null
*/
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
……
/**
* Returns an {@code Optional} with the specified present non-null value.
*
* @param <T> the class of the value
* @param value the value to be present, which must be non-null
* @return an {@code Optional} with the value present
* @throws NullPointerException if value is null
*/
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
<pre lang="text" data-origin="pm_code_preview" style="margin: 0px; padding: 0px; list-style: none; font-family: Courier, "Courier New", monospace; display: block;">
/**
* Returns an {@code Optional} describing the specified value, if non-null,
* otherwise returns an empty {@code Optional}.
*
* @param <T> the class of the value
* @param value the possibly-null value to describe
* @return an {@code Optional} with a present value if the specified value
* is non-null, otherwise an empty {@code Optional}
*/
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
<pre lang="text" data-origin="pm_code_preview" style="margin: 0px; padding: 0px; list-style: none; font-family: Courier, "Courier New", monospace; display: block;">
/**
* Common instance for {@code empty()}.
*/
private static final Optional<?> EMPTY = new Optional<>();
……
/**
* Returns an empty {@code Optional} instance. No value is present for this
* Optional.
*
* @apiNote Though it may be tempting to do so, avoid testing if an object
* is empty by comparing with {@code ==} against instances returned by
* {@code Option.empty()}. There is no guarantee that it is a singleton.
* Instead, use {@link #isPresent()}.
*
* @param <T> Type of the non-existent value
* @return an empty {@code Optional}
*/
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
It is strongly recommended to use Optional.ofNullable(obj)
methods to create Optional
objects and obtain corresponding values.
3. Use of Optional
So far, you already know Optional
the benefits, but how do we use them?
Use an Optional
object that can accept null , that is: use a static engineering method Optional.ofNullable(obj)
to create an object that can allow null values Optional
:
<pre lang="text" data-origin="pm_code_preview" style="margin: 0px; padding: 0px; list-style: none; font-family: Courier, "Courier New", monospace; display: block;">
Optional<People> optional = Optional.ofNullable(people);
Even if people is null, the optional object is an empty object.
If people is not null, Optional.isPresent()
return according to the method true
, and Optional.get()
get the value through the method.
In order to avoid NPE, the
Optional.isPresent()
method has already judged null and returns true if it exists.
<pre lang="text" data-origin="pm_code_preview" style="margin: 0px; padding: 0px; list-style: none; font-family: Courier, "Courier New", monospace; display: block;">
People p = null;
if (optional.isPresent()) {
p = optional.get();
}
Seeing this, you may find that there is no difference between this and the null judgment check.
Later Optional
, when I came into contact with other APIs, I discovered that the following APIs really reflect its value.
3.1 **Optional.map**
Obtaining an attribute from an object is the most common operation. For example, you may need to get the person's name from the people object. Before getting the person's name, you need to check whether the people object is null, as shown below:
<pre lang="text" data-origin="pm_code_preview" style="margin: 0px; padding: 0px; list-style: none; font-family: Courier, "Courier New", monospace; display: block;">
String name = null;
if (null != people) {
name = people.getName();
}
To use Optional.map
it, you can write:
<pre lang="text" data-origin="pm_code_preview" style="margin: 0px; padding: 0px; list-style: none; font-family: Courier, "Courier New", monospace; display: block;">
Optional<People> optional = Optional.ofNullable(people);
Optional<String> stringOptional = optional.map(People::getName);
3.2 **Optional.orElse**
When an object is null, a default value can usually be set in the business so that the process can continue.
pre lang="text" data-origin="pm_code_preview" style="margin: 0px; padding: 0px; list-style: none; font-family: Courier, "Courier New", monospace; display: block;">
String name = null != people ? people.getName() : "Unknown";
Or throw an exception.
<pre lang="text" data-origin="pm_code_preview" style="margin: 0px; padding: 0px; list-style: none; font-family: Courier, "Courier New", monospace; display: block;">
if (null != people.getName()) {
throw new RuntimeException();
}
Optional
Class provides two methods orElse
and orElseThrow
, above may conveniently complete the conversion.
pre lang="text" data-origin="pm_code_preview" style="margin: 0px; padding: 0px; list-style: none; font-family: Courier, "Courier New", monospace; display: block;">
// 设置默认值
String name = optional.orElse(new People("Unknown")).getName();
// 抛出异常
String name = optional.orElseThrow(RuntimeException::new).getName();
If optional
empty, the default value or provide an exception is thrown.
3.3 **Optional.filter**
You often need to call a method of an object to view some of its properties. For example, you may need to check whether the person's name is "xcbeyond". In order to perform these operations in a safe manner, you first need to determine whether the people object is null, and then call its method getName, as shown below:
<pre lang="text" data-origin="pm_code_preview" style="margin: 0px; padding: 0px; list-style: none; font-family: Courier, "Courier New", monospace; display: block;">
if (null != people && "xcbeyond".equals(people.getName())) {
System.out.println("ok");
}
Using the method filter provided by the Optional class, it can be refactored well:
pre lang="text" data-origin="pm_code_preview" style="margin: 0px; padding: 0px; list-style: none; font-family: Courier, "Courier New", monospace; display: block;">
optional.filter(people1 -> "xcbeyond".equals(people.getName()))
.ifPresent(x -> System.out.print("ok"));
4. Optional refactoring code
Let's take a look at the code at the beginning of the article:
<pre lang="text" data-origin="pm_code_preview" style="margin: 0px; padding: 0px; list-style: none; font-family: Courier, "Courier New", monospace; display: block;">
String name = "Unknown";
if (null != people) {
if (null != people.getName()) {
name = people.getName();
}
}
return name;
After knowing Optional, refactor the code:
pre lang="text" data-origin="pm_code_preview" style="margin: 0px; padding: 0px; list-style: none; font-family: Courier, "Courier New", monospace; display: block;">
Optional<People> optional = Optional.ofNullable(people);
return optional.map(People::getName).orElse("Unknown");
Combined with Optional and Lambda expressions, it can be clearly seen that after refactoring, the code is more fluent and coherent, and the overall readability of the code is improved.