This is the 7th day of my participation in the August Text Challenge.More challenges in August

Java has introduced the Optional API since 1.8 to avoid NPE (NullPointerException) to some extent. However, many people may not be as proficient with this well-designed API, and may even abuse it. I’ll discuss it in detail below, and then give what I think are the best practices.

Note from the author

First let’s take a look at Optional author Brian Goetz’s explanation of the API:

Our intention was to provide a limited mechanism for library method return types where there needed to be a clear way to represent “no result”, and using null for such was overwhelmingly likely to cause errors.

To avoid null errors, we provide a limited mechanism for explicitly expressing null values.

Basic understanding of

First, Optional is a container for potentially null values, and it handles null reasonably and elegantly. Null has been a hot topic in THE history OF programming, and has been called THE WORST MISTAKE in COMPUTER SCIENCE. You can read this article: THE WORST MISTAKE OF COMPUTER SCIENCE. Prior to Java 1.8, there was no official API that could be used to represent NULL, and if you were careful, you might often need to make the following judgments in your code:

if (null! = user) {//doing something
}
if (StringUtil.isEmpty(string)) {
    //doing something
}
Copy the code

Indeed, there are far too many cases where the return value is null, and if you’re not careful, an NPE can occur, resulting in application terminations, product complaints, and user complaints.

After 1.8, the JDK added Optional to indicate empty results. Essentially nothing has changed, but an expression has been added. Optional specifies an empty static method as option.empty (). Not really. If you look at the implementation, the value in Optional is null, it’s just a layer of Optional, so it’s really a container. The code might look like this:

/ / 1
Optional<User> optionalUser = RemoteService.getUser();
if(! optionalUser.isPresent()) {//doing something 
}
User user = optionalUser.get();

/ / 2
User user = optionalUser.get().orElse(new User());
Copy the code

Looks a little better than before, or at least less stupid. But if you write it as 1, it’s a little bit more verbose.

If you know anything about Kotlin, kotlin’s non-null type is one of their much-hyped “selling points” via var param!! Do mandatory null-checking where it is used, otherwise it will not compile, minimizing NPE. In my opinion, the Optional approach is more elegant and flexible. Optional can also be misleading.

Here are a few inappropriate uses, in my opinion:

Bad Practice

1. Run isPresent() to check if

This is a direct reference to the above example. The if method is no different from the pre-1.8 method. Instead, the return value contains a layer of Optional value, which increases the code complexity without any real benefit. In fact, isPresent() is usually used at the end of stream processing to determine whether a condition is met.

list.stream()
    .filer(x -> Objects.equals(x,param))
    .findFirst()
    .isPresent()
Copy the code

2. Use Optional in method arguments

Before we use something, we have to figure out what problem it was created to solve. If a method parameter can be null, why not override it? The same goes for using constructors. Overloaded business representation is more intuitive.

//don't write method like this
public void getUser(long uid,Optional<Type> userType);

//use Overload
public void getUser(long uid) {
    getUser(uid,null);
}
public void getUser(long uid,UserType userType) {
    //doing something
}

Copy the code

3. Use Optional. Get directly

Optional does not do any null checking or exception handling for you. Using option.get () directly in your code is just as dangerous as not doing any null checking. This can happen when you’re in a hurry to launch or deliver Optional. Here to say more, some people may ask: Party A/business is anxious, and there are many demands, how can he have time to optimize? Because I have encountered in the real work, but the two are not contradictory, because the number of lines of code is not very different, as long as they usually keep learning, are handy things.

4. Used in POJOs

Few people use it this way:

public class User {
    private int age;
    private String name;
    private Optional<String> address;
}
Copy the code

This can cause serialization problems. Optional does not implement serialization itself, nor is it supported by existing JSON serialization frameworks.

5. Use in injected properties

This way of writing is expected to be used by fewer people, but not to the exclusion of imaginative people.

public class CommonService {
    private Optional<UserService> userService;
    
    public User getUser(String name) {
        returnuserService.ifPresent(u -> u.findByName(name)); }}Copy the code

First, dependency injection is mostly under the Spring framework, so using @AutoWired is convenient. But if userServiceset fails, the program should terminate with an exception, not silently, as if nothing was wrong.

Best and Pragmatic Practice

API

Before we get to best practices, let’s take a look at some common apis that Optional provides.

1. empty(a)

Return an Optional container object instead of null. You are advised to use ⭐⭐⭐⭐

2. of(T value)

Creates an Optional object and throws an NPE if value is null. ⭐⭐ is not recommended

3. ofNullable(T value)

As above, create an Optional object, but return option.empty () if value is empty. You are advised to use ⭐⭐⭐⭐⭐

4. get()

Return the value wrapped in Optional. Do not use it directly before emptying. Try not to use it! ⭐

5. orElse(T other)

Again, return the value wrapped in Optional, but if no value is available, return the default you specify. Sounds good, but ⭐⭐ is not recommended

6. orElseGet(Supplier<? extends T> other)

Return the value wrapped in Optional, or default if you can’t get it. It looks the same as 5, but ⭐⭐⭐⭐⭐ is recommended

7. orElseThrow(Supplier<? extends X> exceptionSupplier)

Returns the value wrapped in Optional, and raises the specified exception if it does not get the value. In obstructive service scenarios, ⭐⭐⭐⭐ is recommended

8. isPresent(a)

Check whether there is a value in Optional, return Boolean, useful in some cases, but try not to use it in if judgment bodies. You can use ⭐ ⭐ ⭐

9. ifPresent(Consumer<? super T> consumer)

Check if there is a value in Optional, execute consumer, or do nothing. For daily use, use ⭐⭐⭐⭐

TIPS

First, some ground rules:

  • Don’t declare anythingOptionalInstance attributes
  • Do not use it in any setter or constructorOptional
  • OptionalIs a return type used in business return values or remote calls

1. If a null value is required, use theOptional.empty()

public Optional<User> getUser(String name) {
    if (StringUtil.isNotEmpty(name)) {
        return RemoteService.getUser(name);
    } 
    return Optional.empty();
}
Copy the code

2. Use the orElseGet ()

There are three ways to obtain a value: get() orElse() orElseGet(). It is recommended to use only orElseGet() where necessary.

First, get() cannot be used directly; it needs to be used in conjunction with nulling. And this! =null doesn’t make much difference, just an improvement in expression and abstraction.

Second, why not recommend orElse()? Because orElse() executes the contents of the parentheses anyway, orElseGet() executes only when the main value is null. Here’s an example:

public String getName(a) {
    System.out.print("method called");
}

String name1 = Optional.of("String").orElse(getName()); //output: method called
String name2 = Optional.of("String").orElse(() -> getName()); //output:
Copy the code

If the above example getName() method is a remote call or involves a lot of file IO, you can imagine the cost.

But is orElse() useless? And it isn’t. OrElseGet () needs to build a Supplier, which simply returns a static resource, string, etc.


public static final String USER_STATUS = "UNKNOWN"; .public String findUserStatus(long id) {
    Optional<String> status = ... ; // 
    return status.orElse(USER_STATUS);
}

// Don't write that
public String findUserStatus(long id) {
    Optional<String> status = ... ; // 
    return status.orElse("UNKNOWN");// This creates a new String each time
}
Copy the code

3. Use the orElseThrow ()

This is appropriate for obstructive business scenarios, such as when none of the following operations can be performed without obtaining user information from upstream, and an exception should be thrown. The normal way to write this is to declare null and then manually throw the exception, now can be integrated into a line:

public String findUser(long id) {
    Optional<User> user = remoteService.getUserById(id) ;
    return user.orElseThrow(IllegalStateException::new);
}
Copy the code

4. Execute ifPresent() if not null

This has no performance advantage, but can make code simpler:

// This was the case before
if (status.isPresent()) {
    System.out.println("Status: " + status.get());
}

/ / now,
status.ifPresent(System.out::println);
Copy the code

5. Don’t abuse it

Some methods are straightforward, but there is no need to add Optional to add complexity.

public String fetchStatus(a) {
    String status = getStatus() ;
    return Optional.ofNullable(status).orElse("PENDING");
}

// A simple state
public String fetchStatus(a) {
    String status = ... ;
    return status == null ? "PENDING" : status;
}
Copy the code

6. Do not use Optional in collections

First, NULL can be one of the elements of a collection and is not illegal; Second, the collection type itself already has a complete empty expression, and wrapping a layer of Optional is just too complicated to benefit from. For example, Map already has an Orelse-like API like getOrDefault().

conclusion

The appearance of Optional makes Java null expression ability a step closer, good horse with saddle, reasonable use can avoid a lot of NPE, save a lot of manpower and resources. The above content is also I query a lot of information, while learning while writing output, if there are mistakes, please don’t hesitate to give advice.