当前位置 : 首页 » 文章分类 :  开发  »  Java8 Optional 笔记

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

阅读
评论
1.6k
阅读预计7分钟
创建日期 2018-08-29
修改日期 2023-03-28
类别

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论