建议收藏,Java Optional如何使用的最佳实践

这里写目录标题

    • 真实业务场景
    • Optional简介
      • 方法列表
    • 演示示例
      • 1. 创建Optional对象
      • 2. 判断值是否存在
      • 3. 非空表达式
      • 4. 设置(获取)默认值
      • 5. 获取值
      • 6. 过滤Filter使用
      • 7. 映射map的使用
    • 总结

真实业务场景

先来看这样一个业务中的场景:
在这里插入图片描述
这里取值可能为空,如果为空改成数字0L,如果交给你你怎么处理?
❌ 错误示范
你会不会把这个流拆开使用for循环,在用ifelse判断,如下:

if(item.getTaskRecordId != null) {
	// 处理逻辑
}

✅ 使用Optional轻松解决这种空值的问题

List<String> engineTaskIds = executeCaseRecords.stream()
        .filter(item -> !TaskStatus.isStatusFinished(item.getStatus()))
        .map(item -> SystemUtil.engineTaskId(Optional.ofNullable(item.getTaskRecordId()).orElse(0L),item.getCaseRecordId(), item.getId()))
        .collect(Collectors.toList());

Optional简介

Optional是Java8提供的为了解决null安全问题的一个API。善用Optional可以使我们代码中很多繁琐、丑陋的设计就可以变得十分优雅,比如上面的真实业务场景的案例。
建议收藏,Java Optional如何使用的最佳实践_第1张图片

方法列表

按照常用的顺序排列如下:

  • Optional.orElseGet(Supplier other) 如果存在则返回值,否则调用other并返回该调用的结果。
  • Optional.orElse(T other) 如果存在返回,不存在返回other
  • Optional.ofNullable(T value) value可以为空
  • Optional.empty() 创建一个空实例对象,不存在值。不存在值的意义不是里面存null,null也是值。
  • Optional.of(T value) 返回一个包含value的对象。value不能是null,否则会抛NPE异常
  • Optional.get() 返回非空值,如果value为空,抛出NoSuchElementException
  • Optional.isPresent() 值存在返回true,不存在返回false,存在null,也是false
  • Optional.ifPresent(Consumer consumer) 如果存在值,则使用该值调用指定的消费者,否则什么都不做。
  • Optional.filter(Predicate predicate) 如果存在一个值,并且该值与给定的Predicate匹配,则返回描述该值的Optional,否则返回空Optional。
  • Optional.map(Function mapper) 如果存在值,则对其应用所提供的映射函数,如果结果非空,则返回描述结果的Optional。否则返回一个空的Optional。
  • Optional.flatMap(Function> mapper) 如果存在值,则对其应用所提供的与Optional相关的映射函数,返回该结果,否则返回空的Optional。这个方法类似于map(Function),但是所提供的映射器的结果已经是一个Optional,并且如果调用,flatMap不会用额外的Optional来包装它。
  • Optional.orElseThrow(Supplier exceptionSupplier) throws X 返回包含的值(如果存在),否则抛出由提供的提供程序创建的异常。

最常用到的方法是orElese()

演示示例

1. 创建Optional对象

方式一:创建个空对象

Optional<String> empty = Optional.empty();
System.out.println(empty.isPresent());  // false
System.out.println(empty.orElse("hello"));  // hello

方式二: 创建一个非空的 Optional 对象

Optional<String> opt = Optional.of("hello world");
System.out.println(opt); // 输出:Optional[hello world]

方式三:可以使用静态方法 ofNullable() 创建一个即可空又可非空的 Optional 对象

Optional<String> optOrNull = Optional.ofNullable(null);
System.out.println(optOrNull); // 输出:Optional.empty

2. 判断值是否存在

可以通过方法 isPresent() 判断一个 Optional 对象是否存在,如果存在,该方法返回 true,否则返回 false——取代了 obj != null 的判断。

Optional<String> opt = Optional.of("hello world");
System.out.println(opt.isPresent()); // 输出:true

Optional<String> optOrNull = Optional.ofNullable(null);
System.out.println(opt.isPresent()); // 输出:false

3. 非空表达式

Optional 类有一个非常现代化的方法——ifPresent(),允许我们使用函数式编程的方式执行一些代码,因此,我把它称为非空表达式。如果没有该方法的话,我们通常需要先通过 isPresent() 方法对 Optional 对象进行判空后再执行相应的代码:

Optional<String> optOrNull = Optional.ofNullable(null);
if (optOrNull.isPresent()) {
    System.out.println(optOrNull.get().length());
}

有了 ifPresent() 之后,情况就完全不同了,可以直接将 Lambda 表达式传递给该方法,代码更加简洁,更加直观。

Optional<String> opt = Optional.of("hello world");
opt.ifPresent(str -> System.out.println(str.length())); // 如果是null这里就不会执行了

4. 设置(获取)默认值

有时候,我们在创建(获取) Optional 对象的时候,需要一个默认值,orElse() 和 orElseGet() 方法就派上用场了。orElse() 方法用于返回包裹在 Optional 对象中的值,如果该值不为 null,则返回;否则返回默认值。该方法的参数类型和值得类型一致。

String name = Optional.ofNullable(null).orElse("hello world");
System.out.println(name); // 输出:hello world

orElseGet() 方法与 orElse() 方法类似,但参数类型不同。如果 Optional 对象中的值为 null,则执行参数中的函数。

String name = Optional.ofNullable(null).orElseGet(()->"hello world");
System.out.println(name); // 输出:hello world

从输出结果以及代码的形式上来看,这两个方法极其相似,但是需要注意一点的是orElese里的代码一定会执行但不一定返回,orEleseGet就不一定会执行,参考下面代码:

public static void main(String[] args) {
   String name = "hello";
   System.out.println("orElse");
   String name2 = Optional.ofNullable(name).orElse(getDefaultValue());

   System.out.println("orElseGet");
   String name3 = Optional.ofNullable(name).orElseGet(TaskCancelServiceImpl::getDefaultValue);

}

public static String getDefaultValue() {
  	System.out.println("getDefaultValue");
  	return "getDefaultValue";
}

输出内容:
orElse
getDefaultValue
orElseGet
可以看到orEleseGet部分内容没有执行。所以这种情况orElese方法性能更佳

5. 获取值

直观从语义上来看,get() 方法才是最正宗的获取 Optional 对象值的方法,但很遗憾,该方法是有缺陷的,因为假如 Optional 对象的值为 null,该方法会抛出 NoSuchElementException 异常。这完全与我们使用 Optional 类的初衷相悖,所以不建议使用get方法。

public class GetOptionalDemo {
    public static void main(String[] args) {
        String name = null;
        Optional<String> optOrNull = Optional.ofNullable(name);
        System.out.println(optOrNull.get());
    }
}

这段程序在运行时会抛出异常:

Exception in thread "main" java.util.NoSuchElementException: No value present
	at java.base/java.util.Optional.get(Optional.java:141)
	at com.cmower.dzone.optional.GetOptionalDemo.main(GetOptionalDemo.java:9)

尽管抛出的异常是 NoSuchElementException 而不是 NPE,但在我们看来,显然是在“五十步笑百步”。建议使用orElseGet() 方法获取 Optional 对象的值。

6. 过滤Filter使用

使用Optional可以用来检验参数的合法性

public void setName(String name) throws IllegalArgumentException {
    this.name = Optional.ofNullable(name)
                        .filter(User::isNameValid)
                        .orElseThrow(()->new IllegalArgumentException("Invalid username."));
}

7. 映射map的使用

先看下不使用map的代码:

public static String getChampionName(Competition comp) throws IllegalArgumentException {
    if (comp != null) {
        CompResult result = comp.getResult();
        if (result != null) {
            User champion = result.getChampion();
            if (champion != null) {
                return champion.getName();
            }
        }
    }
    throw new IllegalArgumentException("The value of param comp isn't available.");
}

使用map改造过后的代码是什么样的:

public static String getChampionName(Competition comp) throws IllegalArgumentException {
    return Optional.ofNullable(comp)
            .map(Competition::getResult)  // 相当于c -> c.getResult(),下同
            .map(CompResult::getChampion)
            .map(User::getName)
            .orElseThrow(()->new IllegalArgumentException("The value of param comp isn't available."));
}

参考对比会发现使用Optional可以优雅解决很多npe场景的一些问题。

总结

使用Optional,推荐使用方法:orElese,orEleseGet,ofNullable,filter和map。

你可能感兴趣的:(Java篇,java,Optional,java技巧)