Blog address blog.imuxuan.com/archives/86

Sentenced to empty the disaster

As a brick remover, we must be all too familiar with nullPointerexceptions. Don’t tell me you rarely nullPointerExceptions unless you like them.

But for many apes, NullPointerException is also the closest member of the Exception family.

To avoid nullPointerExceptions coming to us, we often do the following.

if(data ! =null) {
    do sth.
}
Copy the code

If you use an object more than once in a class, you might want to do something about it, so:

The “ninth Wonder of the World” was born. Maybe you will think, you are not the only one in the project who will do this, then press Command+Shift+F and you will see the truth:

What, we have close to 10,000 lines of code that are nulling?

All right, now, let’s get down to business.

NullObject mode

We call it a “void disaster” for the numerous nullifies in the project, which have a very bad impact on code quality and cleanliness.

So, how does this work? You’ve probably heard of NullObject, but it’s not our weapon of the day, but NullObject is something to talk about.

What is NullObject mode?

In object-oriented computer programming, a null object is an object with no referenced value or with defined neutral (“null”) behavior. The null object design pattern describes the uses of such objects and their behavior (or lack thereof).

The above analysis is from Wikipedia.

The NullObject pattern was first published in the Programming Pattern Language series. In general, in object-oriented languages, null-checking is used to determine whether objects are empty before they are called, because the required method cannot be called on an empty reference.

A typical implementation of the empty object pattern is shown below (image from the network) :

Example code is as follows (named after the network, ha ha how lazy it is) :

Nullable is a Nullable interface used to determine whether an Object is Null. In the nullobject mode, an Object that is Null is wrapped as an Object, which Nullable implements all methods of the original Object.

public interface Nullable {
    
    boolean isNull(a);
    
}
Copy the code

This interface defines the behavior of the business object.

public interface DependencyBase extends Nullable {

    void Operation(a);

}
Copy the code

This is the real class of the object, implementing the business action interface DependencyBase and the Nullable object action interface.

public class Dependency implements DependencyBase.Nullable {

    @Override
    public void Operation(a) {
        System.out.print("Test!");
    }

    @Override
    public boolean isNull(a) {
        return false; }}Copy the code

This is an empty object, a null implementation of the behavior of the original object.

public class NullObject implements DependencyBase{

    @Override
    public void Operation(a) {
        // do nothing
    }

    @Override
    public boolean isNull(a) {
        return true; }}Copy the code

When used, empty objects can be called through factory calls, or they can be called through other methods such as reflection (which usually takes a few more milliseconds) without going into detail here.

public class Factory {
    
    public static DependencyBase get(Nullable dependencyBase){
        if (dependencyBase == null) {return new NullObject();
        }
        return newDependency(); }}Copy the code

This is an example of how to use objects without NullPointerException. Instead of NullPointerException, we can use objects directly without worrying about NPE.

public class Client {

    public void test(DependencyBase dependencyBase){ Factory.get(dependencyBase).Operation(); }}Copy the code

NullObject is a NullObject that can be used to NullObject objects. NullObject is a NullObject that can be used to NullObject objects. NullObject is a NullObject that can be NullObject objects. No more tedious work to define empty object interfaces and empty exclusive implementation classes.

.NR Null Object

NR Null Object is an Android Studio, IntelliJ IDEA, PhpStorm, WebStorm, PyCharm, RubyMine, AppCode, CLion, GoLand, DataGrip and other IDEA plugin Intellij. It can easily and quickly generate components required by its empty object mode according to existing objects. Its functions are as follows:

  • Analyze the methods that the selected class can declare as an interface;
  • Abstract the public interface;
  • Create empty objects, automatic implementation of public interface;
  • Some functions can be nullable declaration;
  • Can append functions to generate again;
  • Automatic function naming conventions

Let’s take a look at an example:

You can automatically Generate the corresponding Null Object component by right-clicking on the generated Object class and selecting Generate and NR Null Object.

So how do you get this plug-in?

installation

You can install it directly from the Plugins repository in the Preferences of the IDEA.

Select Preferences → Plugins → Browse Repositories

Search NR Null Oject or Null Oject for fuzzy query and click Install on the right to restart IDEA.

Optional

Thank you for your comments. Another way to gracefully short is to use the Java8 feature /Guava’s Optional option to gracefully short.

A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value.

A container object that may or may not contain non-null values. If there is a value, isPresent () will return true and get () will return that value.

Without further ado, let me give you an example.

We have code like this, we need to get Info from Test2, but with the parameter Test4, we need to apply layer after layer, each layer can get an object that is empty, the final code looks like this.

    public String testSimple(Test4 test) {
        if (test == null) {
            return "";
        }
        if (test.getTest3() == null) {
            return "";
        }
        if (test.getTest3().getTest2() == null) {
            return "";
        }
        if (test.getTest3().getTest2().getInfo() == null) {
            return "";
        }
        return test.getTest3().getTest2().getInfo();
    }
Copy the code

But with Optional, the whole thing is different.

    public String testOptional(Test test) {
        return Optional.ofNullable(test).flatMap(Test::getTest3)
                .flatMap(Test3::getTest2)
                .map(Test2::getInfo)
                .orElse("");
    }
Copy the code

1.Optional.ofNullable(test), if test is null, return a singleton empty Optional object, if not null return an Optional wrapper object, Optional wraps test;

    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }
Copy the code

FlatMap (Test::getTest3) checks whether Test is empty. If it is empty, continue to return the singleton Optional object in step 1, otherwise call Test’s getTest3 method.

    public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
        Objects.requireNonNull(mapper);
        if(! isPresent())return empty();
        else {
            returnObjects.requireNonNull(mapper.apply(value)); }}Copy the code

3. FlatMap (Test3::getTest2)

FlatMap (Test2::getInfo) is similar to flatMap, but flatMap requires Test3::getTest2 to return an Optional type. FlatMap does not require multiple wraps.

    public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if(! isPresent())return empty();
        else {
            returnOptional.ofNullable(mapper.apply(value)); }}Copy the code

5. OrElse (” “); Gets the value in the map, if not empty, return value directly, if empty, return the passed parameter as the default value.

public T orElse(T other) {
    returnvalue ! =null ? value : other;
}
Copy the code

How about using Optional? Is our code suddenly clean and tidy? If you look at this code, you might wonder, but Optional has the advantage of being able to call a long and complicated short. However, using Optional for simple shorts can also increase code reading costs, coding costs, and learning costs for new team members. After all, Optional is not yet as popular as RxJava, and it has some limitations.

If you want to use Java8 Optional, ensure that android API level is 24 or higher.

You can also import Google’s Guava directly. (What is Guava? Tips from the authorities)

Guava is a set of core libraries that includes new collection types (such as multimap and multiset), immutable collections, a graph library, functional types, an in-memory cache, and APIs/utilities for concurrency, I/O, hashing, primitives, reflection, string processing, and much more!

A reference, like this:

    dependencies {
      compile : 'com. Google. Guava guava: 27.0 jre'
      // or, for Android:
      api : 'com. Google. Guava guava: 27.0 android'
    }
Copy the code

IDEA, however, is colored yellow by default, prompting you to migrate Guava expressions to the Java Api.

Of course, you can also Kill the Yellow prompt by searching for “Guava” in Preferences.

There are many other tips for using Optional, and you can check out Guava and Java8 books and documentation.

Using Optional has the following advantages:

  1. Wrap defensive programming code beautifully
  2. Chain calls
  3. Effectively avoid null Pointers in program code

But it also has some disadvantages:

  1. Popularity is not ideal, and new team members need to learn the cost
  2. The introduction of Guava in Android requires everyone on the team to either handle the IDEA default prompt or tolerate the yellow prompt
  3. Sometimes code reading might look something like this:

Kotlin

Of course, Kotlin features excellent empty security and can be used well with Java, like this:

test1? .test2? .test3? .test4Copy the code

If you’re already using Kotlin, you don’t have to write defensive nulls. If you’re not already using Kotlin, it’s not recommended to go straight to Kotlin for the sake of elegance.