Java8特性Optional使用实践

一、Optional定义

        A container object which may or may not contain a non-null value。这是Optional类中官方的定义,从此可以看出,optinal相当于一个容器,它所包含的内容可以为空也可以不为空。java8推出Optional类是受到Google Guava的启发,主要用来优雅的处理空指针异常,让我们的代码结构更加清晰,避免因为频繁的空判断,造成代码层级变深,难以阅读。

二、Optional使用场景

【注意】:以下使用为实际使用场景,不包含对Optional中各方法的介绍,请先自行阅读官方对各方法的介绍

1.使用Optional优化多层调用
需求:有三个实体,User1、User2、User3,User2为User1一个属性,User3为User2一个属性,User3包含两个属性分别为年龄、姓名,定义一个方法入参为User1,功能是当年龄不为空时输出年龄,否则抛出异常。
不使用Optional时的三种常见实现方式:
    public void getUserName(User1 user) {
        // 第一种方式
        if (user != null && user.getUser2() != null && user.getUser2().getUser3() != null && StringUtils.isNotBlank(user.getUser2().getUser3().getName())) {
            log.info("一般方式获取用户姓名:{}", user.getUser2().getUser3().getName());
        } else {
            throw new RuntimeException("获取用户姓名失败");
        }

        // 第二种方式
//        if (user != null) {
//            User2 user2 = user.getUser2();
//            if (user2 != null) {
//                User3 user3 = user2.getUser3();
//                if (user3 != null && StringUtils.isNotBlank(user3.getName())) {
//                    log.info("一般方式获取用户姓名:{}", user3.getName());
//                } else {
//                    throw new RuntimeException("获取用户姓名失败");
//                }
//            } else {
//                throw new RuntimeException("获取用户姓名失败");
//            }
//        } else {
//            throw new RuntimeException("获取用户姓名失败");
//        }


        // 第三种方式
//        if (user == null){
//            throw new RuntimeException("获取用户姓名失败");
//        }
//        if (user.getUser2() == null){
//            throw new RuntimeException("获取用户姓名失败");
//        }
//        if (user.getUser2().getUser3() == null){
//            throw new RuntimeException("获取用户姓名失败");
//        }
//        if (StringUtils.isBlank(user.getUser2().getUser3().getName())){
//            throw new RuntimeException("获取用户姓名失败");
//        }
//        log.info("一般方式获取用户姓名:{}", user.getUser2().getUser3().getName());
    }

使用Optional的实现方式:

public void getUserNameOptional(User1 user) {
    String s = Optional.ofNullable(user)
            .map(User1::getUser2)
            .map(User2::getUser3)
            .map(User3::getName)
            .filter(StringUtils::isNotBlank)
            .orElseThrow(() -> new RuntimeException("获取用户姓名失败"));
    log.info("optinal获取用户姓名:{}", s);
}

2.使用stream加optional对集合进行过滤
public void getUserNameNotNull(List<User1> list) {
    // 常规
    List<User1> collect1 = list.stream().filter(user1 -> !Objects.isNull(user1)
            && !Objects.isNull(user1.getUser2())
            && !Objects.isNull(user1.getUser2().getUser3())
            && StringUtils.isNotBlank(user1.getUser2().getUser3().getName())).collect(Collectors.toList());
    log.info("一般方式:{}", JSON.toJSONString(collect1));

    List<User1> collect = list.stream().filter(user -> Optional.ofNullable(user)
            .map(User1::getUser2)
            .map(User2::getUser3)
            .map(User3::getName)
            .filter(StringUtils::isNotBlank).isPresent())
            .collect(Collectors.toList());
    log.info("optional方式:{}", JSON.toJSONString(collect));
}
3.值不为空时执行特定操作
public void writeLog(User1 user) {
    Optional.ofNullable(user).ifPresent(u -> log.info(JSON.toJSONString(u)));
}
4.使用Optional增强mybatis-plus,对对象类型查询结果进行包装
第一种方式:自己创建一个父mapper,继承BaseMapper并使用Optional对BaseMapper中提供的各通用查询方法进行封装,其他mapper均继承自定义的父mapper。

    public User3 queryData(String name) {
        // 将mapper中继承BaseMapper替换为继承自己封装后的
        Optional<User3> optionalUser1 = user3Mapper.getOne(new QueryWrapper<User3>().lambda().eq(User3::getName, name));
        return optionalUser1.orElseThrow(() -> new RuntimeException("未获取到用户信息")).setAge(20);
    }

第二种方式:使用Optional对每一条sql的查询结果进行封装,高版本的mybatis-plus支持结果使用Optional包装

    public User3 queryData(String name) {

        Optional<User3> optionalUser2 = userMapper.getInfo(name);
        return optionalUser2.orElseThrow(() -> new RuntimeException("未获取到用户信息")).setAge(20);
    }
5.使用Optional + stream流,对集合类型的查询结果进行包装、处理
一般方式:
    public User3 queryDataList(String name) {
        List<User3> list = user3Mapper.selectList(new QueryWrapper<User3>().lambda().eq(User3::getName, name));
        if (!CollectionUtils.isEmpty(list)) {
            list.stream().findFirst().orElseThrow(() -> new RuntimeException("未获取到用户信息")).setAge(20);
        }
        throw new RuntimeException("未获取到用户信息");
    }
使用Optional包装:
    public User3 queryDataList(String name) {
        Optional<List<User3>> infoList = user3Mapper.list(new QueryWrapper<User3>().lambda().eq(User3::getName, name));
        return infoList.orElseThrow(() -> new RuntimeException("未获取到用户信息"))
                .stream().findFirst().orElseThrow(() -> new RuntimeException("未获取到用户信息")).setAge(20);
    }
6.Optional在配置变量注入时的使用

例:有一个配置项,如果配置了值则使用配置值,没有配置则需要从根据上下文的变量进行计算

@Value("${optional.properties:}")
private Optional<Integer> value;
public Integer getPropertiesValue() {
    int a = 1;
    int b = 2;
    return value.orElseGet(() -> a * b);
}

三、需要避免的使用方式

1.不要使用使用Optional对确认不为空的值进行包装
一般来说,使用Optional包装,代表我们不确定他的内容是否一定存在,也就是说该值可能为空,后续调用方在使用此值时,根据自己的业务需要考虑是否需要进行空判断。而当某一个值必然不为空时,如果继续使用Optional进行包装,使用方可能误以为值可能为空依然进行空检测,那么可能增加使用方的工作量。

2.避免使用Optional.isPresent()来检查实例是否存在
isPresent()方法只会返回质是否为空的布尔值,不支持链式调用其他方案,如果单纯为了使用他进行空检测,而包装一层Optional那是没有意义的不如直接使用!= null直接判断

3.避免使用Optional.get()方式来获取实例对象
该方法在获取值之前不会进行值为空的判断,可能造成空指针异常,这与我们使用Optional时的初衷希望优雅的处理空指针背道而驰,建议使用其他两个获取值的方式获取值。

4.不要将Optional对象初始化为null
永远不要将一个Optional对象初始化为null,这样会造成使用时还需要额外对这个容器进行判空操作,不仅无法优化我们的代码,甚至会让我们的代码更加复杂。

5.避免使用Optional作为类或者实例的属性,Optional没有实现Serializable接口,如果进行序列化会产生异常
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值