New features of JDK1.8: Are you still writing null pointer check code repeatedly? Use Optional now

image.png

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:

image

Yes, you read that right, it is at the NullPointerExceptiontop of the list.

Null ReferenceThe 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 NullPointerExceptionsimply 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 NullPointerExceptionthe 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 NullPointerExceptionwrite 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.

image

When the variable exists, Optionalthe class simply encapsulates the class. When the variable does not exist, the missing value will be modeled as an "empty" Optionalobject, 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 trueand the value is obtained through the Optional.get()method.

OptionalThe construction method is privatethat you cannot directly use new to create an Optionalobject, but can only use the Optionalprovided static method to create it.

The creation methods provided by Optional are as follows:

  • Optional.of(obj): If the object is null, it will be thrown NullPointerException.

  • Optional.ofNullable(obj): If the object is null, an EMPTY Optional object instance without a value will be created (new Optional<>()).

  • Optional.empty(): Same as Optional.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, &quot;Courier New&quot;, 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, &quot;Courier New&quot;, 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, &quot;Courier New&quot;, 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 Optionalobjects and obtain corresponding values.

3. Use of Optional

So far, you already know Optionalthe benefits, but how do we use them?

Use an Optionalobject 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, &quot;Courier New&quot;, 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, &quot;Courier New&quot;, 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, &quot;Courier New&quot;, monospace; display: block;">

String name = null;

if (null != people) {

 name = people.getName();

}

To use Optional.mapit, you can write:

<pre lang="text" data-origin="pm_code_preview" style="margin: 0px; padding: 0px; list-style: none; font-family: Courier, &quot;Courier New&quot;, 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, &quot;Courier New&quot;, 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, &quot;Courier New&quot;, monospace; display: block;">

if (null != people.getName()) {

 throw new RuntimeException();

}

OptionalClass provides two methods orElseand 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, &quot;Courier New&quot;, monospace; display: block;">

// 设置默认值

String name = optional.orElse(new People("Unknown")).getName();

// 抛出异常

String name = optional.orElseThrow(RuntimeException::new).getName();

If optionalempty, 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, &quot;Courier New&quot;, 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, &quot;Courier New&quot;, 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, &quot;Courier New&quot;, 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, &quot;Courier New&quot;, 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.

Guess you like

Origin blog.csdn.net/weixin_47067712/article/details/108150075