Reasonable use of Optional to avoid NPE

1. What is Optional

What kind of exception is most likely to appear in Java, it must be NullPointerException, the null pointer is like a time bomb, always brings us some troubles, in the development process will encounter the need to judge the Null value to prevent the situation of the null pointer, in the past The way is either to throw an exception, or if{}else{}, until the appearance of Optional, you can solve the NPE problem more elegantly.
First, let's take a look at the description of the API by Brian Goetz, the author of Optional:

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.

The general meaning of the translation is: Optional provides a limited mechanism to allow the return value of the class library method to clearly express whether there is a value or not, avoiding errors caused by null in many cases, so try to use the tool class Optional in Java8+, and simplify the code in specific
scenarios logic.

2. What are the common methods in Optional

static method

  • Optional.of (T value): Create an Optional specifying a non-null value for the specified value. It should be noted that the incoming parameter cannot be null, otherwise a NullPointerException will be thrown.
  • Optional.ofNullable() : Create an Optional object for the specified value. If the specified parameter is null, no exception is thrown, and an empty Optional object is returned directly.

object method

  • isPresent() : Determine whether the optional is empty, if it is empty, return false, otherwise return true
  • get() : Returns the Optional if it has a value, otherwise throws NoSuchElementException
  • ifPresent(Consumer c) : If the optional is not empty, pass the object in the optional to the Consumer function orElse(T other): If the optional is not empty, return the object in the optional; if it is null, return other This is the default value
  • orElseGet(Supplier other) : If optional is not empty, return the object in optional; if it is null, use the Supplier function to generate the default value other
  • orElseThrow(Supplier exception) : If the optional is not empty, return the object in the optional; if it is null, throw the exception generated by the Supplier function
  • filter(Predicate p) : If the optional is not empty, execute the assertion function p, if the result of p is true, return the original optional, otherwise return the empty optional
  • map(Function<T, U> mapper) : If optional is not empty, map object t in optional to another object u, and store u in a new optional container.
  • flatMap(Function<T,Optional<U>> mapper) : Same as above, when optional is not empty, map object t to another optional, difference: map will automatically put u in optional, and flatMap You need to manually create an optional for u

3. Optional Misuse Scenarios

1. Use isPresent() directly for if check

There is no difference between writing in this way and directly judging the space, but it increases the trouble

public class User{
    
    
	private String name;
	private Integer age;

	public String getName() {
    
    
		return name;
	}

	public void setName(String name) {
    
    
		this.name = name;
	}
}
// 错误使用Optional
	User u=null;
	Optional<User> userOpt=Optional.ofNullable(u);
	if(userOpt.isPresent()){
    
    
		String name=u.getName();
		System.out.println(name);
	}
	// 直接判断
	if(u !=null){
    
    
		String name=u.getName();
		System.out.println(name);
	}

Acceptable writing method: It is recommended to use ifPresent(Consumer<? super T> consumer) to determine whether there is a value in the Optional, and execute the consumer if there is a value, otherwise do nothing. It is not recommended to use isPresent() directly for if check to judge

userOpt.ifPresent(user -> System.out.println(user.getName()));

In fact, isPresent() is more recommended to be used at the end of stream processing to determine whether the conditions are met

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

2. Use Optional in method parameters or in POJO

Optional itself does not implement serialization, and most of the existing JSON serialization frameworks do not provide support for it.

public class User {
    
    
    private String name;
	private Integer age;
    // 不建议
    private Optional<String> address;
}

3. Use Optional.get directly

Optional will not help you do any empty judgment or exception handling. If you use Optional.get() directly in the code, it is as dangerous as not doing any empty judgment. When there is no value, a NoSuchElementException will be thrown

User u=null;
Optional<User> userOpt=Optional.ofNullable(u);
// 不建议
System.out.println(userOpt.get().getName());

The result is:
insert image description here
there are three ways to get the value: get(), orElse(), orElseGet()
get() can't be used directly, it needs to be used in conjunction with empty judgment. This is actually not much different from !=null, just improved in expression and abstraction.
The difference between orElse() and ElseGet()
Performance issues: the content in the brackets will be executed anyway, orElseGet() is only executed when the main value is empty orElseGet() needs to build a Supplier Improper
use, again null pointer, when the parameter of orElse is When it can be calculated indirectly. Although this statement is a bit far-fetched (because it is not orElse that causes the null pointer exception), using orElseGet can indeed avoid this situation

class User {
    
    
    // 中文名
	private String chineseName;
	// 英文名
	private EnglishName englishName;
}

class EnglishName {
    
    
    // 全名
    private String fullName;
    // 简写
    private String shortName;
}

If we have a User class now, when a user registers an account, they need to provide their own Chinese name or English name, or both, we abstract an EnglishName class, which contains the full name and abbreviation of the English name (because some English names are really too long up). Now, we hope to have a User#getName() method, which can be implemented as follows: when the user only provides a Chinese name, the englishName attribute is null at this time, but in orElse, englishName.getShortName() will always be executed . In getName2(), this risk is not.

class User {
    
    
    // ... 之前的内容
    public String getName1() {
    
    
        return Optional.ofNullable(chineseName)
                .orElse(englishName.getShortName());
    }
    public String getName2() {
    
    
        return Optional.ofNullable(chineseName)
                .orElseGet(() -> englishName.getShortName());
    }
}

If you simply return a static resource, string, etc., you can directly return the static resource and use orElse().

4. Used in injected properties

// 一般不建议
public class CommonService {
    
    
    private Optional<UserService> userService;
}

Fourth, the correct use of Optional posture

1. Assign a property of an object to another entity object

// 调用服务层查询数据库获得目标对象
ProdSku prodSku = prodSKUService.getFirstProdSku(p.getProdId());
if (prodSku != null) {
    
    
    prodAPP.setPrice(prodSku.getPrice());
}
// 使用Optional 和函数式编程,一次完成
Optional.ofNullable(prodSku).ifPresent(p -> prodAPP.setPrice(p.getPrice()));

2. When the value cannot be obtained, return the default you specified

If the specified value is fixed, it is recommended to use orElse() explicitly, and if the specified value is ambiguous or requires a lot of calculations, it is recommended to use orElseGet()

2.1 Judging the size of a collection, but afraid that the collection is null

List<User> list=null;
Optional<List<User>> optional=Optional.ofNullable(list);
int size = optional.map(List::size).orElse(0);
// 输出0
System.out.println(size);
// 直接调用集合的size方法就有可能造成 NullPointerException 空指针异常
System.out.println(list.size());

2.2 Entity class get method initialization assignment

// 当传给前端同学一个字段时候,并不希望这个值出现null,但数据库里确实就是null的情况
public BigDecimal getAccount() {
    
    
        if (account == null) {
    
    
            account = BigDecimal.ZERO;
        }
        return account;
}
// 代替:
public BigDecimal getAccount() {
    
    
        return Optional.ofNullable(account).orElse(BigDecimal.ZERO) ;
}

2.3 Convert between types, return a default value when there is no value

String price=null;
Double c=Optional.ofNullable(price).map(a->Double.valueOf(a)).orElse(0.00);
System.out.println(c);
// 直接转换必定空指针错误
Double b=Double.valueOf(price);

2.4 Get the Chinese character information corresponding to the object

If it is null, it will return directly without obtaining it. Whether a is null or the operation of a is null, it can go to orElse

Optional<Integer> optional=Optional.ofNullable(super.getAduitStatus());
        return optional.map(a->AduitStatusEnum.name(String.valueOf(a))).orElse(null);

3. Multi-layer attribute acquisition (obtaining the value of a multi-layer nested entity, the most obvious example of benefits)

 //+++++++++++++++++++++++模拟这个要取的对象从其他接口传过来是不是null++++++++++++++++++++++++++++++++++++
    InetSocketAddress inetSocketAddress = new InetSocketAddress(2);
    String re="小明取不到值";
    if(inetSocketAddress !=null){
    
    
        if(inetSocketAddress.getAddress() !=null){
    
    
            if(inetSocketAddress.getAddress().getHostName() !=null){
    
    
                String name2 = inetSocketAddress.getAddress().getHostName().toUpperCase();
                if(StringUtils.isNotBlank(name2) ){
    
    
                    re=name2;
                }
            }
        }
    }
    System.out.println(re);

    Optional<InetSocketAddress> optional=Optional.ofNullable(inetSocketAddress);
    String op= optional.map(a ->a.getAddress()).map(b ->b.getHostName()).map(c->c.toUpperCase()).orElse("小王也取不到值");
    System.out.println(op);

 //+++++++++++++++++++++++模拟这个要取的对象从其他接口传过来是null++++++++++++++++++++++++++++++++++++
    String re2="小明取不到值";
    InetSocketAddress inetSocketAddress2 = null;
    if(inetSocketAddress2 !=null){
    
    
        if(inetSocketAddress2.getAddress() !=null){
    
    
            if(inetSocketAddress2.getAddress().getHostName() !=null){
    
    
                String name2 = inetSocketAddress2.getAddress().getHostName().toUpperCase();
                if(name2 ==null){
    
    
                    re2=name2;
                }
            }
        }
    }
    System.out.println(re2);

    Optional<InetSocketAddress> optional2=Optional.ofNullable(inetSocketAddress2);
    String op2= optional2.map(a ->a.getAddress()).map(b ->b.getHostName()).map(c->c.toUpperCase()).orElse("小王也取不到值");
    System.out.println(op2);

The running result is shown in the figure below
insert image description here

4. Recommended for blocking business scenarios

BigDecimal stock = Optional.ofNullable(lcStockObj).map(a -> a.getAssetNum())
                           .orElseThrow(() -> new RuntimeException("库存数据不存在,出入库失败"));

Reference link: https://blog.csdn.net/rhythm_1/article/details/121716010

Guess you like

Origin blog.csdn.net/neusoft2016/article/details/130744787