Java8 Optional 笔记
Java8 Optional 笔记
Java Optional
空指针异常是导致 Java 应用程序失败的最常见原因。以前,为了解决空指针异常,Google 公司著名的 Guava 项目引入了 Optional 类,Guava 通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。
受到 Google Guava 的启发,Optional 类已经成为 Java 8 类库的一部分。Optional 实际上是个容器:它可以保存类型 T 的值,或者仅仅保存 null。Optional 提供很多有用的方法,这样我们就不用显式进行空值检测。
of() 创建不是null的Optional
ofNullable() 创建可为null的Optional
Optional.of() 或者 Optional.ofNullable() 都可以创建 Optional 对象,差别在于 of 不允许参数是null,而 ofNullable 则无限制。
isPresent() 非空时执行lambda
检查是否有值的另一个选择是 ifPresent() 方法。该方法除了执行检查,还接受一个 Consumer 参数,如果对象不是空的,就对执行传入的 Lambda 表达式:
Optional.ofNullable(user)
.ifPresent(u -> assertEquals(user.getEmail(), u.getEmail()));
这个例子中,只有 user 用户不为 null 的时候才会执行断言。
ifPresentOrElse(Java 9+)
Java 9 新增 public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)
,值存在时执行action,不存在时执行 emptyAction
User name 不存在时设为 newName,存在的话校验与 newName 是否一致,不一致抛异常。
User user = findUser();
String newName = "new name";
Optional.ofNullable(user.getName())
.filter(StringUtils::isNotBlank)
.ifPresentOrElse(name -> {
if (!StringUtils.equals(strategyType, newName)) {
throw new InvalidParameterException("名字不一致");
}
}, () -> user.setName(newName));
orElse() 值为空时的默认值
orElse(),它的工作方式非常直接,如果 Optional 有值则返回该值,否则返回传递给它的参数值
User user = null;
User user2 = new User("anna@gmail.com", "1234");
User result = Optional.ofNullable(user).orElse(user2);
orElseGet() 值为空时执行lambda
第二个同类型的 API 是 orElseGet() —— 其行为略有不同。这个方法会在 Optional 有值的时候返回值,如果没有值,它会执行作为参数传入的 Supplier(供应者) 函数式接口,并将返回其执行结果:
User result = Optional.ofNullable(user).orElseGet( () -> user2);
orElse() 和 orElseGet() 的不同之处
当 Optional 对象为空而返回 orElse() 或 orElseGet() 中的默认值时,行为并无差异,都会执行其中的代码。
但当 Optional 对象不为空时,orElse() 还会执行其中的代码,而 orElseGet() 不会执行。
public void givenPresentValue_whenCompare_thenOk() {
User user = new User("john@gmail.com", "1234");
logger.info("Using orElse");
User result = Optional.ofNullable(user).orElse(createNewUser());
logger.info("Using orElseGet");
User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser());
}
这个示例中,两个 Optional 对象都包含非空值,两个方法都会返回对应的非空值。不过,orElse() 方法仍然创建了 User 对象。与之相反,orElseGet() 方法不创建 User 对象。
orElseThrow() 值不存在时抛异常
值不存在则抛出异常,存在则什么不做,有点类似Guava的Precoditions
public void whenThrowException_thenOk() {
User result = Optional.ofNullable(user)
.orElseThrow( () -> new IllegalArgumentException());
}
map() 值映射
User user = new User("anna@gmail.com", "1234");
String email = Optional.ofNullable(user)
.map(u -> u.getEmail()).orElse("default@gmail.com");
filter() 过滤
filter() 方法用于判断 Optional 对象是否满足给定条件,如果 filter() 方法中的 Lambda 表达式成立,filter() 方法会返回当前 Optional 对象值,否则,返回一个值为空的 Optional 对象。
public String getValueByKeyOrDefault(String key, String defaultValue) {
return Optional.ofNullable(getByKey(key))
.filter(dictVO -> StringUtils.isNotBlank(dictVO.getDictValue()))
.map(DictVO::getDictValue).orElse(defaultValue);
}
Optionals
org.springframework.data.util.Optionals
ifPresentOrElse
第一个参数 optional 是一个 Optional,如果 optional 有值则执行 consumer,否则执行 runnable
public static <T> void ifPresentOrElse(Optional<T> optional, Consumer<? super T> consumer, Runnable runnable) {
Assert.notNull(optional, "Optional must not be null!");
Assert.notNull(consumer, "Consumer must not be null!");
Assert.notNull(runnable, "Runnable must not be null!");
if (optional.isPresent()) {
optional.ifPresent(consumer);
} else {
runnable.run();
}
}
使用示例:
Optionals.ifPresentOrElse(
Optional.ofNullable(userDao.findByUserId(123)),
userDO -> {
log.info("查询到user {}", userDO)
},
() -> {
log.info("未查询到user");
userDao.createUser();
}
);
使用示例
为了防止从 data 中取出的 Double 类型数据为空导致的类型转换Exception,用 Optional 赋默认值0
bean.setIntegerValue((int)(double)Optional.ofNullable(data.getDoubleValue()).orElse(0.0));
bean.setIntegerValue((int)(double)Optional.ofNullable(data.getDoubleValue()).orElse(2.0));
chinaRegion 非空则取其name,否则赋空
bean.setAddress(Optional.ofNullable(chinaRegion)
.map(ChinaRegion::getName)
.orElse(""));
两个map连续转换
Optional.ofNullable(result)
.map(Object::toString)
.map(String::toLowerCase)
.orElse("");
如果枚举 serviceOrderStatus 非空则取其描述信息,否则赋空
String orderStatusDesc = Optional.ofNullable(serviceOrderStatus)
.map(ServiceOrderStatus::getDesc)
.orElse("");
先取request实体中的account_id,没有的话从userBean中取
Long accountId = Optional.ofNullable(request.getAccountId())
.orElse(Optional.ofNullable(userBean)
.map(UserBean::getAccountId)
.orElse(null));
分页默认值
paging.setOffset((long)Optional.ofNullable(request.getOffset()).orElse(DEFAULT_PAGE_OFFSET));
paging.setCount(Optional.ofNullable(request.getCount()).orElse(DERAULT_PAGE_COUNT));
集合resultList按订单起始时间排序,没有起始时间则按结束时间,没有结束时间则使用默认值
// 整合后订单按创建时间倒序排序
Collections.sort(resultList, new Comparator<BaseBean>() {
@Override
public int compare(Order order1, Order order2) {
Date order1CompareDate = Optional.ofNullable(order1.getOrderStartTime())
.orElse(Optional.ofNullable(order1.getOrderEndTime()).orElse(DEFAULT_ORDER_START_TIME));
Date order2CompareDate = Optional.ofNullable(order2.getOrderStartTime())
.orElse(Optional.ofNullable(order2.getOrderEndTime()).orElse(DEFAULT_ORDER_START_TIME));
return order2CompareDate.compareTo(order1CompareDate);
}
});
根据姓名集合查订单集合,如果结果不为空的话,取出其中的订单号构成List,否则新建List
List<Order> orderList = orderMapper.queryOrdersByNameList(request.getNameList());
List<String> orderNoList = Optional.ofNullable(orderList)
.map(ol -> ol.stream().map(Order::getOrderNo).collect(Collectors.toList()))
.orElse(Lists.newArrayList());
从request中取起始时间和结束时间的Unix时间戳,有的话转为Date,没有的话设为null
Date startTime = Optional.ofNullable(request.getStartTimestamp()).map(t -> new Date(t * 1000)).orElse(null);
Date endTime = Optional.ofNullable(request.getEndTimestamp()).map(t -> new Date(t * 1000)).orElse(null);
Optional 套 Optional
连环 Optional,先取 account_id,没有的话根据 user_id 查 account_id,再没有的话取 null
Long accountId = Optional.ofNullable(request.getAccountId())
.orElse(Optional.ofNullable(baseUserMapper.selectAccountId(request.getUserId()))
.orElse(null));
如果不是 InnerUser,index 为 null
如果是 InnerUser,查询同名的最后一个user,从名字里获取 NO. 之后的数字+1 是index,没有同名的则 index 为1
AtomicInteger index = ofNullable(user)
.filter(u -> t.getType() == InnerUser)
.map(u -> ofNullable(userDao.findLatestByName(u.getName()))
.map(sameName -> StringUtils.substringAfterLast(sameName.getName(), "NO."))
.map(Integer::parseInt)
.map(i -> new AtomicInteger(i + 1))
.orElse(new AtomicInteger(1)))
.orElse(null);
代替 if 非空判断
Optional.ofNullable(req.getName())
.filter(StringUtils::isNotBlank)
.ifPresent(name -> jpaQuery.where(qUserDO.name.like(String.format("%%%s%%", name))));
上一篇 ELK使用笔记
下一篇 Apache-Kafka
页面信息
location:
protocol
: host
: hostname
: origin
: pathname
: href
: document:
referrer
: navigator:
platform
: userAgent
: