先来看这样一个业务中的场景:
这里取值可能为空,如果为空改成数字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是Java8提供的为了解决null安全问题的一个API。善用Optional可以使我们代码中很多繁琐、丑陋的设计就可以变得十分优雅,比如上面的真实业务场景的案例。
按照常用的顺序排列如下:
最常用到的方法是orElese()
方式一:创建个空对象
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
可以通过方法 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
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这里就不会执行了
有时候,我们在创建(获取) 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方法性能更佳
直观从语义上来看,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 对象的值。
使用Optional可以用来检验参数的合法性
public void setName(String name) throws IllegalArgumentException {
this.name = Optional.ofNullable(name)
.filter(User::isNameValid)
.orElseThrow(()->new IllegalArgumentException("Invalid username."));
}
先看下不使用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。