Null causes problems in Java

A NullPointerException is one of the most common and annoying exceptions in Java programming. It’s ingrained in many of our programs that we add if-else checks wherever a null pointer might be generated, but it causes us a lot of trouble

  • Java itself is strongly typed, but NULL breaks this rule and can be assigned to any object
  • Java is designed to make programs insensitive to Pointers, but null Pointers are an exception
  • It’s going to make the code bloated, it’s going to be all over the placeif-elseEmpty check, even multi-layer nesting, code readability decreases
  • Null by itself is meaningless. It doesn’t mean anythingThere is no

The first two points need no special explanation, the last two points can be illustrated by an example: if a person owns a mobile phone, each mobile phone has a manufacturer, each manufacturer will have a name, represented by a class:

public class Person { private Phone phone; public Phone getPhone() { return phone; } } public class Phone { private Producer producer; public Producer getProducer() { return producer; } } public class Producer { private String name; public String getName() { return name; }}Copy the code

In this example, let’s say we need the name of the manufacturer that made the phone

public String getPhoneProducerName(Person person) {
    return person.getPhone().getProducer().getName();
}
Copy the code

Since not everyone has a phone, a NullPointerException may occur when getProducer() is called.

A design language is designed to describe the world, and in this case some people have phones, and some people may not, so the call to Person.getPhone () should return both yes and none. Now null is returned to indicate none. However, a call to getProducer() throws an exception, which is not very logical. So null is an appropriate expression for all

A common practice in this case is to do a null check, even for every place where a null pointer might occur.

Public String getPhoneProducerName(Person Person) {if (person.getphone () == null) {return "No name "; } if (person.getphone ().getProducer() == null) {return "No name "; } return person.getPhone().getProducer().getName(); }Copy the code

Here I have tried to reduce the level of the code. If I use if-else, the level of the code will be deeper and the code will be less readable.


Optional’s brief introduction

Having made so much fun of the current situation, it’s time to offer our solution. Beginning to come out, still holding pipa half mask; What is that Optional? Let’s take a look at it step by step.

Optional itself is simply a wrapper around an object. If the object is empty, an empty Optional is built. So Optional includes both the presence and the absence of Optional, and you can look at the example above

public class Person { private Optional<Phone> phone; public Optional<Phone> getPhone() { return phone; } } public class Phone { private Producer producer; public Producer getProducer() { return producer; } } public class Producer { private String name; public String getName() { return name; }}Copy the code

Some people don’t have mobile phones. Some people do. The phone itself must have a manufacturer, the manufacturer must have a name, so these two don’t need to be packaged with Optional. Here we’ll see that using Optional enriches the semantics of the code and makes it more realistic.

When we call phone.getProducer().getName(), there is no need to check null pointer. If NullPointerException occurs here, it indicates that there is something wrong with the data itself, which does not conform to the reality, and the problem should be exposed. Instead of hiding the problem like the code above.


Optional common method to use

1. Optional creation method
Optional<Person> empty = Optional.empty(); // declare an empty Optional Optional<Person> Person = Optional. Of (new Person()); // Wrap Person Optional<Person> person2 = Optional. Of (null); OptionalPerson = optionalPerson = option.ofNullable (null); // Allow null, return an empty OptionalCopy the code
2. How to obtain the Optional value
  • The map, flatMap

First, let’s redefine the Phone class to have a model number in addition to the manufacturer.

public class Phone { private String model; private Producer producer; public Producer getProducer() { return producer; } public String getModel() { return model; }}Copy the code

When we need to get the phone model, we can do this:

Optional<Phone> optionalPhone = Optional.of(new Phone());
Optional<String> model = optionalPhone.map(Phone::getModel);
Copy the code

When we need to get the Phone model from the Person object, we can think of something like this:

Optional<Person> optionalPerson = Optional.of(new Person());
optionalPerson.map(Person::getPhone).map(Phone::getModel);
Copy the code

When we write it out, the compiler doesn’t pass. Because Person::getPhone returns an Optional , optionalPerson.map(Person::getPhone) returns Optional
>, How can I return Optional
instead of Optional ?

Another method, flatMap, is needed here. The difference between flatMap and Map, WHEN I started to learn, I saw all kinds of explanations on the Internet are very round, look very dizzy, finally directly open the source code, found that the implementation is very simple, very easy to understand, look at the source code:

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

The map method returns with a layer of Optional; FlatMap returns the value of the function directly. The result must be Optional. So in the previous example we called flatMap directly and the result returned is Optional

Optional<Person> optionalPerson = Optional.of(new Person());
optionalPerson.flatMap(Person::getPhone).map(Phone::getModel);
Copy the code
  • Take out theOptionalGet, orElse, orElseGet, orElseThrow, ifPresent
  1. Get () : Call this method if you know that Optional has a value, and raise an exception if Optional does notNoSuchElementException; Therefore, it is recommended not to call this method if there is a null value, as this is no different from doing a null check
  2. OrElse (T other) : provides a default value. The default value is returned if the current value does not exist
  3. orElseGet(Supplier
    other) : The supper function is called when the value does not exist. This method is appropriate if there is more logic to return this default value.
  4. orElseThrow(Supplier
    exceptionSupplier) : throws a custom exception when the value is null
  5. ifPresent(Consumer<? Super T> consumer) : will be called when the value is not emptyconsumerFunction, if the value is null, then this method does nothing, right
  • Filter Filters out the objects that meet the conditions

Suppose we need to filter out the phone model IOS phone and print out the model, the code is as follows:

Person person = new Person(Optional.of(new Phone("IOS")));
        Optional<Person> optionalPerson = Optional.of(person);
        optionalPerson.flatMap(Person::getPhone)
                .filter(phone -> "IOS".equals(phone.getModel()))
                .map(Phone::getModel)
                .ifPresent(System.out::println);
Copy the code

conclusion

  1. We discussed the problem of NULL in Java programs
  2. Introduced in Java8OptionalTo represent the yes and no cases and how to initialize them
  3. ExemplifyOptionalIs often used in the

I am a newbie, if there is anything wrong, please point it out in the comments section