​JDK1.8 new feature: still writing null pointer check code repeatedly? Use Optional now

1. Foreword

As a Java programmer, whether a novice or a veteran expert, he must have encountered all kinds of abnormal errors. In a foreign article, the ranking list of exception types is counted, as shown in the following figure:

Yes, you're right. NullPointerException tops the list.

Charles Antony Richard Hoare, the inventor of Null Reference, said: "I call it my billion dollar error. This is the result of the invention of empty reference in 1965... It has led to countless errors, vulnerabilities and system crashes, which may cause billion dollars of pain and damage in the last 40 years."

This may seem exaggerated, but it is indisputable that NullPointerException is a pain in the hearts of programmers. It is not that it is difficult to solve, but that we need to pay an extra price to solve it.

I still remember when you first entered the industry, you encountered the bug caused by NullPointerException in three or two days. After solving one, you encountered it in another place. This also slowly makes you understand that you should not trust any "object", especially what others provide to you, and add judgment where you use it, so you can be more relieved. So the code usually becomes 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;">

String name = "Unknown";

if (null != people) {

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

 name = people.getName();

 }

}

return name;

In this way, although you don't have to worry about NullPointerException, too many judgment statements really make people feel numb and the code becomes bloated. If the object is too complex and there are objects in the object, do you want to continue to judge layer by layer?

The exciting thing is that jdk1 8 introduces a new class java util. Optional < T >, with the API provided by the optional class, we don't have to worry about NullPointerException anymore, let alone 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, the car variable inside the Person class should not be declared as car. When someone doesn't have a car, assign a null reference to it, but should be declared as Optional directly as shown in the figure below.

When a variable exists, the optional class simply encapsulates the class. When the variable does not exist, the missing value will be modeled as an "empty" optional object by the method optional Empty() returns. Optional. The empty () 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 value or null value. We need to pass the object instance into the container. If the value exists, optional The ispresent () method returns true and passes the optional Get () method to get the value.

The construction method of Optional is private. You cannot directly use new to create an Optional object. You can only use the static method provided by Optional.

The creation method provided by Optional is as follows:

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

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

  • Optional.empty(): equivalent to optional ofNullable(null).

The source code 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) method to create an Optional object and obtain the corresponding value.

3. Use of Optional

So far, you already know the benefits of Optional, but how should we use it?

Use an Optional object that can accept null, that is, use the static engineering method Optional Ofnullable (obj), create an Optional object that allows null values:

<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, according to optional The ispresent () method returns true and passes the optional Get () method to get the value.

To avoid NPE, optional Ispresent () method has 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();

}

See here, you may find that this is no different from the null judgment check.

Later, when I came into contact with other Optional APIs, I found that the following APIs really embody its value.

3.1 **Optional.map**

Getting a property from an object is the most common operation. For example, you may need to get a person's name from the people object. Before getting the person 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();

}

Use optional The map method can be written 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;">

Optional<People> optional = Optional.ofNullable(people);

Optional<String> stringOptional = optional.map(People::getName);

3.2 **Optional.orElse**

When an object is null, the business can usually set a default value to continue the process.

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();

}

The Optional class provides two methods orElse and orElseThrow, which can facilitate the above 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;">

// Set default values

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

// Throw exception

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

If optional is empty, provide a default value or throw an exception.

3.3 **Optional.filter**

You often need to call the method of an object and 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 way, you first need to judge 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 filter method provided by the Optional class, you can refactor 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 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 the 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 is obvious that after refactoring, the code is more fluent and coherent, and the overall readability of the code is improved.

Tags: Java Javascript Vue Multithreading object

Posted by habs20 on Sat, 21 May 2022 10:20:38 +0300