Java8 Features Lecture 3: How to use the Optional class to elegantly solve business npe problems

Java8 Features Lecture 3: How to use the Optional class to elegantly solve business npe problems

This article is the third lecture of Java8 features, how to use the Optional class to elegantly solve business npe problems.

1. Background

As a Java programmer, everyone may have the experience of calling a method and getting a return value but cannot directly use the return value as a parameter to call other methods. We must first determine whether the return value is null, and only if it is not empty can it be used as a parameter of other methods . This is exactly what some Guava-like external APIs try to solve. Some JVM programming languages ​​such as Scala, Ceylon, etc. have already solved this problem in the core API.

New versions of Java, such as Java 8 introduce a new Optional class. The Javadoc description of the Optional class is as follows:

This is a nullable container object. The isPresent() method returns true if the value exists, and calling the get() method returns the object.

2. The methods contained in the Optional class

2.1、of

Create an Optional for non-null values.

The of method creates the Optional class through a factory method. It should be noted that the parameter passed in when creating an object cannot be null. If the incoming parameter is null, NullPointerException is thrown.

//调用工厂方法创建Optional实例
Optional<String> name = Optional.of("Sanaulla");
//传入参数为null,抛出NullPointerException.
Optional<String> someNull = Optional.of(null);

2.2、ofNullable

Creates an Optional for the specified value, or returns an empty Optional if the specified value is null.

ofNullable is similar to the of method, the only difference is that it can accept the case where the parameter is null. Examples are as follows:

//下面创建了一个不包含任何值的Optional实例
//例如,值为'null'
Optional empty = Optional.ofNullable(null);

2.3、isPresent

very easy to understand

Returns true if the value exists, false otherwise.

Code similar to the following:

// isPresent方法用来检查Optional实例中是否包含值
if (name.isPresent()) {
    
    
    //在Optional实例内调用 get() 返回已存在的值
    System.out.println(name.get());//输出Sanaulla
}

2.4、get

Returns the Optional if it has a value, otherwise throws NoSuchElementException.

In the above example, the get method is used to get the value in the Optional instance. Let's look at an example that throws NoSuchElementException:

//执行下面的代码会输出: No value present 
try {
    
    
  	//在空的Optional实例上调用get(),抛出NoSuchElementException
  	System.out.println(empty.get());
} catch (NoSuchElementException ex) {
    
    
	  System.out.println(ex.getMessage());
}

2.5、ifPresent

If the Optional instance has a value, call the consumer for it, otherwise do not process it

To understand the ifPresent method, you first need to understand the Consumer class. In short, the Consumer class contains one abstract method. This abstract method processes the passed in value, but does not return a value . Java8 supports passing parameters directly through lambda expressions without interfaces.

If the Optional instance has a value, the call to ifPresent() can accept an interface segment or a lambda expression. Code similar to the following:

// ifPresent方法接受lambda表达式作为参数。
// lambda表达式对Optional的值调用consumer进行处理。
name.ifPresent((value) -> {
    
    
  System.out.println("The length of the value is: " + value.length());
});

// 上述代码可以简化为
name.ifPresent((value) -> System.out.println("The length of the value is: " + value.length()));

2.6、orElse

Returns a value if available, otherwise returns the specified other value.

If the Optional instance has a value, it is returned, otherwise it returns the parameter passed in by the orElse method . Examples are as follows:

//如果值不为null,orElse方法返回Optional实例的值。
//如果为null,返回传入的消息。
//输出: There is no value present!
System.out.println(empty.orElse("There is no value present!"));
//输出: Sanaulla
System.out.println(name.orElse("There is some value!"));

2.7、orElseGet

orElseGet is similar to the orElse method, the difference is the default value obtained. The orElse method takes the incoming string as the default value, and the orElseGet method can accept the implementation of the Supplier interface to generate the default value . Examples are as follows:

//orElseGet与orElse方法类似,区别在于orElse传入的是默认值,
//orElseGet可以接受一个lambda表达式生成默认值。
//输出: Default Value
System.out.println(empty.orElseGet(() -> "Default Value"));
//输出: Sanaulla
System.out.println(name.orElseGet(() -> "Default Value"));

2.8、orElseThrow

If there is a value, it will be returned, otherwise an exception created by the supplier interface will be thrown .

In the orElseGet method, we pass in a Supplier interface. However, in orElseThrow we can pass in a lambda expression or method to throw an exception if the value does not exist. Examples are as follows:

try {
    
    
    //orElseThrow与orElse方法类似。与返回默认值不同,
    //orElseThrow会抛出lambda表达式或方法生成的异常 
    empty.orElseThrow(ValueAbsentException::new);
} catch (Throwable ex) {
    
    
    //输出: No value present in the Optional instance
    System.out.println(ex.getMessage());
}

ValueAbsentException is defined as follows:

class ValueAbsentException extends Throwable {
    
    

    public ValueAbsentException() {
    
    
        super();
    }

    public ValueAbsentException(String msg) {
    
    
        super(msg);
    }

    @Override
    public String getMessage() {
    
    
        return "No value present in the Optional instance";
    }
}

2.9、map

The map method documentation states the following:

If there is a value, call the mapping function on it to get the return value. If the return value is not null, create an Optional containing the return value of mapping as the return value of the map method, otherwise return an empty Optional.

The map method is used to perform a series of operations on the value of the Optional instance. The operation is passed in through a set of lambda expressions that implement the Function interface. If you are not familiar with the Function interface, you can refer to this blog, java8/Stream streaming computing from entry to proficiency/functional programming practice . An example of the map method is as follows:

// map方法执行传入的lambda表达式参数对 Optional实例的值进行修改。
// 为lambda表达式的返回值创建新的Optional实例作为map方法的返回值。
Optional<String> upperNameOpt = name.map((value) -> value.toUpperCase());
System.out.println(upperNameOpt.orElse("No value found"));

2.10、flatMap

If there is a value, execute the mapping function for it to return an Optional type return value, otherwise return an empty Optional. flatMap is similar to the map(Function) method, the difference is that the mapper return value in flatMap must be Optional . At the end of the call, flatMap will not encapsulate the result with Optional.

The flatMap method is similar to the map method, the difference is that the return value of the mapping function is different. The return value of the mapping function of the map method can be any type T, while the mapping function of the flatMap method must be Optional.

Referring to the map function, an example rewritten using flatMap is as follows:

//flatMap与map(Function)非常类似,区别在于传入方法的lambda表达式的返回类型。
//map方法中的lambda表达式返回值可以是任意类型,在map函数返回之前会包装为Optional。 
//但flatMap方法中的lambda表达式返回值必须是Optionl实例。 
upperNameOpt = name.flatMap((value) -> Optional.of(value.toUpperCase()));
System.out.println(upperNameOpt.orElse("No value found"));//输出SANAULLA

2.11、filter

The filter method filters the value of the Optional instance by passing in the qualification conditions. The document description is as follows:

Returns an Optional containing the value if there is a value and the assertion condition is satisfied, otherwise an empty Optional is returned.

After reading this, you probably already know how to pass a piece of code to the filter method. Yes, a lambda expression can be passed in here. For the filter function we should pass in a lambda expression that implements the Predicate interface.

Now let's take a look at the various uses of filter. The following example introduces two situations that meet the qualification conditions and do not meet them:

//filter方法检查给定的Option值是否满足某些条件。
//如果满足则返回同一个Option实例,否则返回空Optional。
Optional<String> longName = name.filter((value) -> value.length() > 6);
System.out.println(longName.orElse("The name is less than 6 characters"));//输出Sanaulla

//另一个例子是Optional值不满足filter指定的条件。
Optional<String> anotherName = Optional.of("Sana");
Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);
//输出: name长度不足6字符
System.out.println(shortName.orElse("The name is less than 6 characters"));

3. Optional elegantly solves business NPE problems

3.1, a comprehensive example

public class OptionalDemo {
    
    

    public static void main(String[] args) {
    
    
      //创建Optional实例,也可以通过方法返回值得到。
      Optional<String> name = Optional.of("Sanaulla");

      //创建没有值的Optional实例,例如值为'null'
      Optional empty = Optional.ofNullable(null);

      //isPresent方法用来检查Optional实例是否有值。
      if (name.isPresent()) {
    
    
          //调用get()返回Optional值。
          System.out.println(name.get());
      }

      try {
    
    
          //在Optional实例上调用get()抛出NoSuchElementException。
          System.out.println(empty.get());
      } catch (NoSuchElementException ex) {
    
    
          System.out.println(ex.getMessage());
      }

      //ifPresent方法接受lambda表达式参数。
      //如果Optional值不为空,lambda表达式会处理并在其上执行操作。
      name.ifPresent((value) -> {
    
    
          System.out.println("The length of the value is: " + value.length());
      });

      //如果有值orElse方法会返回Optional实例,否则返回传入的错误信息。
      System.out.println(empty.orElse("There is no value present!"));
      System.out.println(name.orElse("There is some value!"));

      //orElseGet与orElse类似,区别在于传入的默认值。
      //orElseGet接受lambda表达式生成默认值。
      System.out.println(empty.orElseGet(() -> "Default Value"));
      System.out.println(name.orElseGet(() -> "Default Value"));

      try {
    
    
          //orElseThrow与orElse方法类似,区别在于返回值。
          //orElseThrow抛出由传入的lambda表达式/方法生成异常。
          empty.orElseThrow(ValueAbsentException::new);
      } catch (Throwable ex) {
    
    
        System.out.println(ex.getMessage());
      }

      //map方法通过传入的lambda表达式修改Optonal实例默认值。 
      //lambda表达式返回值会包装为Optional实例。
      Optional<String> upperName = name.map((value) -> value.toUpperCase());
      System.out.println(upperName.orElse("No value found"));

      //flatMap与map(Funtion)非常相似,区别在于lambda表达式的返回值。
      //map方法的lambda表达式返回值可以是任何类型,但是返回值会包装成Optional实例。
      //但是flatMap方法的lambda返回值总是Optional类型。
      upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));
      System.out.println(upperName.orElse("No value found"));

      //filter方法检查Optiona值是否满足给定条件。
      //如果满足返回Optional实例值,否则返回空Optional。
      Optional<String> longName = name.filter((value) -> value.length() > 6);
      System.out.println(longName.orElse("The name is less than 6 characters"));

      //另一个示例,Optional值不满足给定条件。
      Optional<String> anotherName = Optional.of("Sana");
      Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);
      System.out.println(shortName.orElse("The name is less than 6 characters"));
    }
}

The output of the above code is as follows:

Sanaulla
No value present
The length of the value is: 8
There is no value present!
Sanaulla
Default Value
Sanaulla
No value present in the Optional instance
SANAULLA
SANAULLA
Sanaulla
The name is less than 6 characters

3.2. Improve the safety of Null in Java 8

Suppose we have a class hierarchy like this:

class Outer {
    
    
    Nested nested;
    Nested getNested() {
    
    
        return nested;
    }
}
class Nested {
    
    
    Inner inner;
    Inner getInner() {
    
    
        return inner;
    }
}
class Inner {
    
    
    String foo;
    String getFoo() {
    
    
        return foo;
    }
}

Resolving deeply nested paths in this structure is a bit cumbersome. We have to write a bunch of null checks to make sure we don't cause a NullPointerException:

Outer outer = new Outer();
if (outer != null && outer.nested != null && outer.nested.inner != null) {
    
    
    System.out.println(outer.nested.inner.foo);
}

We can get rid of all these null checks by taking advantage of Java 8's Optional type. The map method receives a lambda expression of type Function and automatically wraps the result of each function into an Optional object. This allows us to do multiple map operations in one line. Null checks are handled automatically under the hood.

Optional.of(new Outer())
    .map(Outer::getNested)
    .map(Nested::getInner)
    .map(Inner::getFoo)
    .ifPresent(System.out::println);

Another way to achieve the same effect is by using a supplier function to solve the problem of nested paths:

Outer obj = new Outer();
resolve(() -> obj.getNested().getInner().getFoo())
    .ifPresent(System.out::println);

Calling obj.getNested().getInner().getFoo() may throw a NullPointerException. In this case, the exception will be caught and the method will return Optional.empty().

public static <T> Optional<T> resolve(Supplier<T> resolver) {
    
    
    try {
    
    
        T result = resolver.get();
        return Optional.ofNullable(result);
    }
    catch (NullPointerException e) {
    
    
        return Optional.empty();
    }
}

Keep in mind that these two solutions may not be as performant as traditional null checks. But in most cases it won't be a big problem.

Use in business

// 使用方式1
boolean itemPriceDescOption = OptionalUtil.checkNPE(() -> extDTO.getItemPriceDesc()).isPresent();
// 使用方式2
Optional<Long> protocolOptional = OptionalUtil.checkNPE(() -> item.getAgreementId());

4, reference

Original link: javacodegeeks

Translation: ImportNew.com - Gao Junyang

Translation link: http://www.importnew.com/6675.html

Guess you like

Origin blog.csdn.net/qq_28959087/article/details/103795586